secure_protocol.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. package aerotun
  2. import (
  3. "crypto/cipher"
  4. "crypto/hmac"
  5. "crypto/rand"
  6. "crypto/sha256"
  7. "encoding/binary"
  8. "errors"
  9. "io"
  10. "net/netip"
  11. "sync"
  12. "sync/atomic"
  13. "time"
  14. "github.com/flynn/noise"
  15. "golang.org/x/crypto/chacha20poly1305"
  16. "golang.org/x/crypto/hkdf"
  17. )
  18. const (
  19. secureCookie byte = 3
  20. secureHeaderSize = 16
  21. secureVersion = 3
  22. secureInit byte = 1
  23. secureResponse byte = 2
  24. secureRaw byte = 16
  25. secureCompressed byte = 17
  26. secureKeepalive byte = 18
  27. secureRekeyAfter = 90 * time.Second
  28. secureRejectAfter = 180 * time.Second
  29. securePreviousGrace = 30 * time.Second
  30. handshakeMaxSize = 512
  31. )
  32. var errPacket = errors.New("invalid authenticated tunnel packet")
  33. var errHandshakeRate = errors.New("handshake rate limited")
  34. // A 1024-packet window permits bounded reordering using a fixed 128-byte bitmap.
  35. type secureReplay struct {
  36. max uint64
  37. bits [16]uint64
  38. }
  39. func (r *secureReplay) valid(n uint64) bool {
  40. if n == 0 {
  41. return false
  42. }
  43. if n > r.max {
  44. return true
  45. }
  46. if r.max-n >= 1024 {
  47. return false
  48. }
  49. pos := n % 1024
  50. return r.bits[pos/64]&(uint64(1)<<(pos%64)) == 0
  51. }
  52. func (r *secureReplay) accept(n uint64) bool {
  53. if !r.valid(n) {
  54. return false
  55. }
  56. if n > r.max {
  57. if n-r.max >= 1024 {
  58. clear(r.bits[:])
  59. } else {
  60. for missing := r.max + 1; missing < n; missing++ {
  61. pos := missing % 1024
  62. r.bits[pos/64] &^= uint64(1) << (pos % 64)
  63. }
  64. }
  65. r.max = n
  66. }
  67. pos := n % 1024
  68. r.bits[pos/64] |= uint64(1) << (pos % 64)
  69. return true
  70. }
  71. type secureSession struct {
  72. local, remote uint32
  73. tx, rx cipher.AEAD
  74. txPrefix, rxPrefix [16]byte
  75. counter atomic.Uint64
  76. rxMu sync.Mutex
  77. replay secureReplay
  78. born time.Time
  79. peerMTU int
  80. compression bool
  81. }
  82. type secureSessions struct {
  83. current, previous, pending *secureSession
  84. previousUntil time.Time
  85. }
  86. type noiseAttempt struct {
  87. state *noise.HandshakeState
  88. packet []byte
  89. header [16]byte
  90. started, lastSent time.Time
  91. }
  92. type secureProtocol struct {
  93. cookieSecret [32]byte
  94. mu sync.Mutex // Only handshakes/publication, never bulk encryption/decryption.
  95. cfg TunnelInstance
  96. psk []byte
  97. identity noise.DHKey
  98. peer []byte
  99. sessions atomic.Pointer[secureSessions]
  100. attempt *noiseAttempt
  101. lastRequest time.Time
  102. cachedInit [32]byte
  103. cachedResponse []byte
  104. cachedAt time.Time
  105. handshakes atomic.Uint64
  106. }
  107. func newSecureProtocol(cfg TunnelInstance) (*secureProtocol, error) {
  108. psk, err := decodeKey(cfg.PSK)
  109. if err != nil {
  110. return nil, err
  111. }
  112. priv, err := decodeKey(cfg.PrivateKey)
  113. if err != nil {
  114. return nil, err
  115. }
  116. pub, err := decodeKey(cfg.PublicKey)
  117. if err != nil {
  118. return nil, err
  119. }
  120. peer, err := decodeKey(cfg.PeerPublicKey)
  121. if err != nil {
  122. return nil, err
  123. }
  124. p := &secureProtocol{cfg: cfg, psk: psk, identity: noise.DHKey{Private: priv, Public: pub}, peer: peer}
  125. if _, err := rand.Read(p.cookieSecret[:]); err != nil {
  126. return nil, err
  127. }
  128. p.sessions.Store(&secureSessions{})
  129. return p, nil
  130. }
  131. func randomIndex() (uint32, error) {
  132. var b [4]byte
  133. for {
  134. if _, err := rand.Read(b[:]); err != nil {
  135. return 0, err
  136. }
  137. if n := binary.LittleEndian.Uint32(b[:]); n != 0 {
  138. return n, nil
  139. }
  140. }
  141. }
  142. func secureHeader(dst []byte, kind byte, index uint32, n uint64) {
  143. binary.BigEndian.PutUint16(dst, MagicHeader)
  144. dst[2] = 3
  145. dst[3] = kind
  146. binary.BigEndian.PutUint32(dst[4:8], index)
  147. binary.BigEndian.PutUint64(dst[8:16], n)
  148. }
  149. func secureMetadata(index uint32, mtu int, compression bool) []byte {
  150. b := make([]byte, 8)
  151. binary.BigEndian.PutUint32(b, index)
  152. binary.BigEndian.PutUint16(b[4:], uint16(mtu))
  153. if compression {
  154. b[6] = 1
  155. }
  156. return b
  157. }
  158. func parseSecureMetadata(b []byte) (index uint32, mtu int, comp bool, err error) {
  159. if len(b) != 8 || b[7] != 0 || b[6] > 1 {
  160. return 0, 0, false, errPacket
  161. }
  162. index = binary.BigEndian.Uint32(b)
  163. mtu = int(binary.BigEndian.Uint16(b[4:]))
  164. comp = b[6] == 1
  165. if index == 0 || mtu < 576 || mtu > 9000 {
  166. err = errPacket
  167. }
  168. return
  169. }
  170. func (p *secureProtocol) newHandshake(initiator bool, header []byte) (*noise.HandshakeState, error) {
  171. prologue := append([]byte("AeroTUN-v3/Noise-IKpsk2/XChaCha-data-v1\x00"), header...)
  172. cfg := noise.Config{CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256), Pattern: noise.HandshakeIK, Initiator: initiator, Prologue: prologue, PresharedKey: p.psk, PresharedKeyPlacement: 2, StaticKeypair: p.identity}
  173. if initiator {
  174. cfg.PeerStatic = p.peer
  175. }
  176. return noise.NewHandshakeState(cfg)
  177. }
  178. func (p *secureProtocol) authenticateHandshake(b []byte) bool {
  179. if len(b) < 32 || len(b) > handshakeMaxSize {
  180. return false
  181. }
  182. mac := hmac.New(sha256.New, p.psk)
  183. mac.Write([]byte("AeroTUN-v3-handshake-MAC"))
  184. mac.Write(b[:len(b)-16])
  185. return hmac.Equal(mac.Sum(nil)[:16], b[len(b)-16:])
  186. }
  187. func (p *secureProtocol) finishHandshake(b []byte) []byte {
  188. mac := hmac.New(sha256.New, p.psk)
  189. mac.Write([]byte("AeroTUN-v3-handshake-MAC"))
  190. mac.Write(b)
  191. return append(b, mac.Sum(nil)[:16]...)
  192. }
  193. func (p *secureProtocol) initiation(mtu int, force bool) ([]byte, error) {
  194. if p.cfg.Mode != "client" {
  195. return nil, nil
  196. }
  197. p.mu.Lock()
  198. defer p.mu.Unlock()
  199. now := time.Now()
  200. if p.attempt != nil && now.Sub(p.attempt.started) < 5*time.Second {
  201. if now.Sub(p.attempt.lastSent) < time.Second {
  202. return nil, nil
  203. }
  204. p.attempt.lastSent = now
  205. return p.attempt.packet, nil
  206. }
  207. if s := p.sessions.Load().current; s != nil && !force && now.Sub(s.born) < secureRekeyAfter && s.counter.Load() < 1<<60 {
  208. return nil, nil
  209. }
  210. index, err := randomIndex()
  211. if err != nil {
  212. return nil, err
  213. }
  214. var header [16]byte
  215. var nonce [8]byte
  216. if _, err = rand.Read(nonce[:]); err != nil {
  217. return nil, err
  218. }
  219. secureHeader(header[:], secureInit, index, binary.BigEndian.Uint64(nonce[:]))
  220. hs, err := p.newHandshake(true, header[:])
  221. if err != nil {
  222. return nil, err
  223. }
  224. b, _, _, err := hs.WriteMessage(append([]byte(nil), header[:]...), secureMetadata(index, mtu, p.cfg.Compression))
  225. if err != nil {
  226. return nil, err
  227. }
  228. b = append(b, make([]byte, 16)...) // stateless source-address cookie
  229. b = p.finishHandshake(b)
  230. p.attempt = &noiseAttempt{state: hs, packet: b, header: header, started: now, lastSent: now}
  231. return b, nil
  232. }
  233. func deriveDataState(c *noise.CipherState, binding []byte) (cipher.AEAD, [16]byte, error) {
  234. // Export Noise split keys only for domain-separated data-key derivation. Noise
  235. // CipherStates are never used for transport, so there are no competing nonces.
  236. key := c.UnsafeKey()
  237. var material [48]byte
  238. _, err := io.ReadFull(hkdf.New(sha256.New, key[:], binding, []byte("AeroTUN-v3-XChaCha20-Poly1305-data")), material[:])
  239. if err != nil {
  240. return nil, [16]byte{}, err
  241. }
  242. a, err := chacha20poly1305.NewX(material[:32])
  243. var prefix [16]byte
  244. copy(prefix[:], material[32:])
  245. clear(material[:])
  246. clear(key[:])
  247. return a, prefix, err
  248. }
  249. func makeSecureSession(local, remote uint32, mtu int, comp bool, tx, rx *noise.CipherState, binding []byte) (*secureSession, error) {
  250. s := &secureSession{local: local, remote: remote, peerMTU: mtu, compression: comp, born: time.Now()}
  251. var err error
  252. s.tx, s.txPrefix, err = deriveDataState(tx, binding)
  253. if err != nil {
  254. return nil, err
  255. }
  256. s.rx, s.rxPrefix, err = deriveDataState(rx, binding)
  257. return s, err
  258. }
  259. // A responder publishes only a pending receive session until authenticated data
  260. // proves the peer also possesses the PSK and completed the fresh handshake.
  261. func (p *secureProtocol) handleHandshake(b []byte, mtu int, source ...netip.AddrPort) (response []byte, peerMTU int, err error) {
  262. if len(b) < 32 || len(b) > handshakeMaxSize || binary.BigEndian.Uint16(b) != MagicHeader || b[2] != 3 || !p.authenticateHandshake(b) {
  263. return nil, 0, errPacket
  264. }
  265. p.mu.Lock()
  266. defer p.mu.Unlock()
  267. now := time.Now()
  268. switch b[3] {
  269. case secureInit:
  270. if p.cfg.Mode != "server" {
  271. return nil, 0, errPacket
  272. }
  273. if len(b) < 48 {
  274. return nil, 0, errPacket
  275. }
  276. var address netip.AddrPort
  277. if len(source) > 0 {
  278. address = source[0]
  279. }
  280. current := p.addressCookie(b[:16], address, now.Unix()/30)
  281. previous := p.addressCookie(b[:16], address, now.Unix()/30-1)
  282. supplied := b[len(b)-32 : len(b)-16]
  283. if !hmac.Equal(current[:], supplied) && !hmac.Equal(previous[:], supplied) {
  284. out := append([]byte(nil), b[:16]...)
  285. out[3] = secureCookie
  286. out = append(out, current[:]...)
  287. return p.finishHandshake(out), 0, nil
  288. }
  289. fingerprint := sha256.Sum256(b)
  290. if now.Sub(p.lastRequest) < time.Second {
  291. return nil, 0, errHandshakeRate
  292. }
  293. p.lastRequest = now
  294. if fingerprint == p.cachedInit && now.Sub(p.cachedAt) < 5*time.Second {
  295. return p.cachedResponse, 0, nil
  296. }
  297. hs, e := p.newHandshake(false, b[:16])
  298. if e != nil {
  299. return nil, 0, e
  300. }
  301. meta, _, _, e := hs.ReadMessage(nil, b[16:len(b)-32])
  302. if e != nil || !hmac.Equal(hs.PeerStatic(), p.peer) {
  303. return nil, 0, errPacket
  304. }
  305. remote, remoteMTU, comp, e := parseSecureMetadata(meta)
  306. if e != nil || remote != binary.BigEndian.Uint32(b[4:8]) {
  307. return nil, 0, errPacket
  308. }
  309. local, e := randomIndex()
  310. if e != nil {
  311. return nil, 0, e
  312. }
  313. old := p.sessions.Load()
  314. for local == remote || (old.current != nil && local == old.current.local) || (old.previous != nil && local == old.previous.local) {
  315. local, e = randomIndex()
  316. if e != nil {
  317. return nil, 0, e
  318. }
  319. }
  320. var h [16]byte
  321. copy(h[:], b[:16])
  322. h[3] = secureResponse
  323. out, c1, c2, e := hs.WriteMessage(append([]byte(nil), h[:]...), secureMetadata(local, mtu, p.cfg.Compression))
  324. if e != nil {
  325. return nil, 0, e
  326. }
  327. if c1 == nil || c2 == nil {
  328. return nil, 0, errPacket
  329. }
  330. session, e := makeSecureSession(local, remote, remoteMTU, comp && p.cfg.Compression, c2, c1, hs.ChannelBinding())
  331. if e != nil {
  332. return nil, 0, e
  333. }
  334. p.sessions.Store(&secureSessions{current: old.current, previous: old.previous, previousUntil: old.previousUntil, pending: session})
  335. out = p.finishHandshake(out)
  336. p.cachedInit = fingerprint
  337. p.cachedResponse = out
  338. p.cachedAt = now
  339. return out, 0, nil
  340. case secureCookie:
  341. if p.cfg.Mode != "client" || len(b) != 48 || p.attempt == nil || now.Sub(p.attempt.started) > 5*time.Second || !hmac.Equal(b[4:16], p.attempt.header[4:16]) {
  342. return nil, 0, errPacket
  343. }
  344. packet := append([]byte(nil), p.attempt.packet...)
  345. copy(packet[len(packet)-32:len(packet)-16], b[16:32])
  346. packet = p.finishHandshake(packet[:len(packet)-16])
  347. p.attempt.packet = packet
  348. p.attempt.lastSent = now
  349. return packet, 0, nil
  350. case secureResponse:
  351. if p.cfg.Mode != "client" || p.attempt == nil || now.Sub(p.attempt.started) > 5*time.Second || !hmac.Equal(b[4:16], p.attempt.header[4:16]) {
  352. return nil, 0, errPacket
  353. }
  354. hs := p.attempt.state
  355. meta, c1, c2, e := hs.ReadMessage(nil, b[16:len(b)-16])
  356. if e != nil {
  357. p.attempt = nil
  358. return nil, 0, e
  359. }
  360. remote, remoteMTU, comp, e := parseSecureMetadata(meta)
  361. if e != nil {
  362. return nil, 0, e
  363. }
  364. if c1 == nil || c2 == nil {
  365. return nil, 0, errPacket
  366. }
  367. local := binary.BigEndian.Uint32(b[4:8])
  368. if local == remote {
  369. return nil, 0, errPacket
  370. }
  371. session, e := makeSecureSession(local, remote, remoteMTU, comp && p.cfg.Compression, c1, c2, hs.ChannelBinding())
  372. if e != nil {
  373. return nil, 0, e
  374. }
  375. old := p.sessions.Load()
  376. p.sessions.Store(&secureSessions{current: session, previous: old.current, previousUntil: now.Add(securePreviousGrace)})
  377. p.attempt = nil
  378. p.handshakes.Add(1)
  379. return nil, remoteMTU, nil
  380. }
  381. return nil, 0, errPacket
  382. }
  383. func (p *secureProtocol) sealInto(dst []byte, nonce *[24]byte, flags byte, payload []byte) ([]byte, error) {
  384. s := p.sessions.Load().current
  385. if s == nil || time.Since(s.born) > secureRejectAfter {
  386. return nil, errSession
  387. }
  388. kind := secureRaw
  389. switch flags {
  390. case 0:
  391. case FlagCompressedLZ4:
  392. if !s.compression {
  393. return nil, errPacket
  394. }
  395. kind = secureCompressed
  396. case FlagKeepalive:
  397. kind = secureKeepalive
  398. default:
  399. return nil, errPacket
  400. }
  401. if cap(dst) < 16+len(payload)+16 {
  402. return nil, errPacket
  403. }
  404. n := s.counter.Add(1)
  405. if n == 0 || n >= 1<<60 {
  406. return nil, errSession
  407. }
  408. dst = dst[:16]
  409. secureHeader(dst, kind, s.remote, n)
  410. copy(nonce[:16], s.txPrefix[:])
  411. binary.BigEndian.PutUint64(nonce[16:], n)
  412. return s.tx.Seal(dst, nonce[:], payload, dst), nil
  413. }
  414. func (p *secureProtocol) compressionEnabled() bool {
  415. s := p.sessions.Load().current
  416. return s != nil && s.compression
  417. }
  418. func (p *secureProtocol) openInto(dst []byte, nonce *[24]byte, b []byte) (payload []byte, flags byte, peerMTU int, err error) {
  419. if len(b) < 32 || cap(dst) < len(b)-32 || binary.BigEndian.Uint16(b) != MagicHeader || b[2] != 3 {
  420. return nil, 0, 0, errPacket
  421. }
  422. switch b[3] {
  423. case secureRaw:
  424. case secureCompressed:
  425. flags = FlagCompressedLZ4
  426. case secureKeepalive:
  427. flags = FlagKeepalive
  428. default:
  429. return nil, 0, 0, errPacket
  430. }
  431. slots := p.sessions.Load()
  432. index := binary.BigEndian.Uint32(b[4:8])
  433. var s *secureSession
  434. for _, candidate := range []*secureSession{slots.current, slots.pending, slots.previous} {
  435. if candidate != nil && candidate.local == index {
  436. s = candidate
  437. break
  438. }
  439. }
  440. if s == nil || time.Since(s.born) > secureRejectAfter || (s == slots.previous && time.Now().After(slots.previousUntil)) || (flags == FlagCompressedLZ4 && !s.compression) {
  441. return nil, 0, 0, errSession
  442. }
  443. n := binary.BigEndian.Uint64(b[8:16])
  444. if n >= 1<<60 {
  445. return nil, 0, 0, errPacket
  446. }
  447. s.rxMu.Lock()
  448. valid := s.replay.valid(n)
  449. s.rxMu.Unlock()
  450. if !valid {
  451. return nil, 0, 0, errReplay
  452. }
  453. copy(nonce[:16], s.rxPrefix[:])
  454. binary.BigEndian.PutUint64(nonce[16:], n)
  455. payload, err = s.rx.Open(dst[:0], nonce[:], b[16:], b[:16])
  456. if err != nil {
  457. return nil, 0, 0, err
  458. }
  459. if flags == FlagKeepalive && (len(payload) != 2 || binary.BigEndian.Uint16(payload) < 576 || binary.BigEndian.Uint16(payload) > 9000) {
  460. return nil, 0, 0, errPacket
  461. }
  462. s.rxMu.Lock()
  463. valid = s.replay.accept(n)
  464. s.rxMu.Unlock()
  465. if !valid {
  466. return nil, 0, 0, errReplay
  467. }
  468. if s == slots.pending {
  469. p.mu.Lock()
  470. nowSlots := p.sessions.Load()
  471. if nowSlots.pending == s {
  472. p.sessions.Store(&secureSessions{current: s, previous: nowSlots.current, previousUntil: time.Now().Add(securePreviousGrace)})
  473. p.handshakes.Add(1)
  474. }
  475. p.mu.Unlock()
  476. peerMTU = s.peerMTU
  477. }
  478. return
  479. }
  480. // Retain at most the current session, one bounded-grace predecessor, and one
  481. // short-lived pending handshake. Expired keys are no longer reachable by the
  482. // protocol; Go does not guarantee physical erasure of discarded heap objects.
  483. func (p *secureProtocol) expire() {
  484. p.mu.Lock()
  485. defer p.mu.Unlock()
  486. old := p.sessions.Load()
  487. next := *old
  488. now := time.Now()
  489. changed := false
  490. if next.current != nil && now.Sub(next.current.born) > secureRejectAfter {
  491. next.current = nil
  492. changed = true
  493. }
  494. if next.previous != nil && now.After(next.previousUntil) {
  495. next.previous = nil
  496. changed = true
  497. }
  498. if next.pending != nil && now.Sub(next.pending.born) > 5*time.Second {
  499. next.pending = nil
  500. changed = true
  501. }
  502. if changed {
  503. p.sessions.Store(&next)
  504. }
  505. if p.attempt != nil && now.Sub(p.attempt.started) > 5*time.Second {
  506. p.attempt = nil
  507. }
  508. if len(p.cachedResponse) > 0 && now.Sub(p.cachedAt) > 5*time.Second {
  509. p.cachedResponse = nil
  510. p.cachedInit = [32]byte{}
  511. }
  512. }
  513. // A short-lived stateless cookie binds expensive DH work to a reachable UDP
  514. // source. A captured initiation cannot be moved to another source without a new
  515. // PSK MAC. Current/previous 30-second buckets tolerate packet delay.
  516. func (p *secureProtocol) addressCookie(header []byte, address netip.AddrPort, bucket int64) (out [16]byte) {
  517. h := hmac.New(sha256.New, p.cookieSecret[:])
  518. h.Write([]byte("AeroTUN-v3-source-cookie"))
  519. h.Write(header[4:16])
  520. var encoded [64]byte
  521. b := address.AppendTo(encoded[:0])
  522. h.Write(b)
  523. var clock [8]byte
  524. binary.BigEndian.PutUint64(clock[:], uint64(bucket))
  525. h.Write(clock[:])
  526. copy(out[:], h.Sum(nil)[:16])
  527. return
  528. }