connection_state.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package nebula
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log/slog"
  6. "sync"
  7. "sync/atomic"
  8. "github.com/slackhq/nebula/cert"
  9. "github.com/slackhq/nebula/handshake"
  10. "github.com/slackhq/nebula/header"
  11. "github.com/slackhq/nebula/noiseutil"
  12. )
  13. const (
  14. ReplayWindow = 1024
  15. // RehandshakeAfterMessages rolls keys inside the AES-GCM data-volume margin (~2^-36 advantage at 64KB frames).
  16. RehandshakeAfterMessages = uint64(1) << 34
  17. // RejectAfterMessages is the nonce ceiling enforced by noiseutil; a tunnel here is deleted locally, not notified.
  18. RejectAfterMessages = noiseutil.RejectAfterMessages
  19. )
  20. // RehandshakeAfterMessages must stay below RejectAfterMessages so tunnels roll before the hard send stop.
  21. const _ = RejectAfterMessages - RehandshakeAfterMessages
  22. type ConnectionState struct {
  23. eKey noiseutil.CipherState
  24. dKey noiseutil.CipherState
  25. myCert cert.Certificate
  26. peerCert *cert.CachedCertificate
  27. initiator bool
  28. messageCounter atomic.Uint64
  29. window *Bits
  30. decryptLock sync.Mutex
  31. writeLock sync.Mutex
  32. }
  33. // newConnectionStateFromResult builds a fully-populated ConnectionState from a
  34. // completed handshake.Result. It seeds messageCounter and the replay window so
  35. // that the post-handshake message indices already used on the wire don't count
  36. // as missed traffic in the data plane.
  37. func newConnectionStateFromResult(r *handshake.Result) (*ConnectionState, error) {
  38. // Refuse a MessageIndex too big for the replay window: it can only be a bug, and would spin the seed loop below.
  39. if r.MessageIndex >= ReplayWindow {
  40. return nil, fmt.Errorf("handshake message index %d exceeds replay window", r.MessageIndex)
  41. }
  42. ci := &ConnectionState{
  43. myCert: r.MyCert,
  44. initiator: r.Initiator,
  45. peerCert: r.RemoteCert,
  46. eKey: noiseutil.NewCipherState(r.EKey, r.Cipher),
  47. dKey: noiseutil.NewCipherState(r.DKey, r.Cipher),
  48. window: NewBits(ReplayWindow),
  49. }
  50. ci.messageCounter.Add(r.MessageIndex)
  51. for i := uint64(1); i <= r.MessageIndex; i++ {
  52. ci.window.Update(nil, i)
  53. }
  54. return ci, nil
  55. }
  56. func (cs *ConnectionState) MarshalJSON() ([]byte, error) {
  57. return json.Marshal(m{
  58. "certificate": cs.peerCert,
  59. "initiator": cs.initiator,
  60. "message_counter": cs.messageCounter.Load(),
  61. })
  62. }
  63. // NextMessageCounter reserves the next 1-based counter; RejectAfterMessages is the first we refuse, pinned to not wrap.
  64. func (cs *ConnectionState) NextMessageCounter() (uint64, bool) {
  65. c := cs.messageCounter.Add(1)
  66. if c >= RejectAfterMessages {
  67. cs.messageCounter.Store(RejectAfterMessages)
  68. return c, false
  69. }
  70. return c, true
  71. }
  72. func (cs *ConnectionState) Curve() cert.Curve {
  73. return cs.myCert.Curve()
  74. }
  75. func (cs *ConnectionState) Decrypt(l *slog.Logger, messageCounter uint64, out []byte, packet []byte, nb []byte) ([]byte, error) {
  76. var err error
  77. cs.decryptLock.Lock()
  78. result := cs.window.Check(l, messageCounter)
  79. cs.decryptLock.Unlock()
  80. if !result {
  81. return nil, ErrAlreadySeen
  82. }
  83. out, err = cs.dKey.DecryptDanger(out, packet[:header.Len], packet[header.Len:], messageCounter, nb)
  84. if err != nil {
  85. return nil, err
  86. }
  87. cs.decryptLock.Lock()
  88. result = cs.window.Update(l, messageCounter)
  89. cs.decryptLock.Unlock()
  90. if !result {
  91. return nil, ErrAlreadySeen
  92. }
  93. return out, nil
  94. }
  95. // VerifyRelay verifies AEAD protected (but not encrypted) relay frames. packet must be length-checked by the caller.
  96. func (cs *ConnectionState) VerifyRelay(l *slog.Logger, messageCounter uint64, packet []byte, nb []byte) error {
  97. cs.decryptLock.Lock()
  98. result := cs.window.Check(l, messageCounter)
  99. cs.decryptLock.Unlock()
  100. if !result {
  101. return ErrAlreadySeen
  102. }
  103. signedPayload := packet[:len(packet)-cs.dKey.Overhead()]
  104. signatureValue := packet[len(packet)-cs.dKey.Overhead():]
  105. _, err := cs.dKey.DecryptDanger(nil, signedPayload, signatureValue, messageCounter, nb)
  106. if err != nil {
  107. return err
  108. }
  109. cs.decryptLock.Lock()
  110. result = cs.window.Update(l, messageCounter)
  111. cs.decryptLock.Unlock()
  112. if !result {
  113. return ErrAlreadySeen
  114. }
  115. return nil
  116. }