connection_state.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package nebula
  2. import (
  3. "encoding/json"
  4. "log/slog"
  5. "sync"
  6. "sync/atomic"
  7. "github.com/slackhq/nebula/cert"
  8. "github.com/slackhq/nebula/handshake"
  9. "github.com/slackhq/nebula/header"
  10. "github.com/slackhq/nebula/noiseutil"
  11. )
  12. const ReplayWindow = 8192
  13. type ConnectionState struct {
  14. eKey noiseutil.CipherState
  15. dKey noiseutil.CipherState
  16. myCert cert.Certificate
  17. peerCert *cert.CachedCertificate
  18. initiator bool
  19. messageCounter atomic.Uint64
  20. window *Bits
  21. decryptLock sync.Mutex
  22. writeLock sync.Mutex
  23. }
  24. // newConnectionStateFromResult builds a fully-populated ConnectionState from a
  25. // completed handshake.Result. It seeds messageCounter and the replay window so
  26. // that the post-handshake message indices already used on the wire don't count
  27. // as missed traffic in the data plane.
  28. func newConnectionStateFromResult(r *handshake.Result) *ConnectionState {
  29. ci := &ConnectionState{
  30. myCert: r.MyCert,
  31. initiator: r.Initiator,
  32. peerCert: r.RemoteCert,
  33. eKey: noiseutil.NewCipherState(r.EKey, r.Cipher),
  34. dKey: noiseutil.NewCipherState(r.DKey, r.Cipher),
  35. window: NewBits(ReplayWindow),
  36. }
  37. ci.messageCounter.Add(r.MessageIndex)
  38. for i := uint64(1); i <= r.MessageIndex; i++ {
  39. ci.window.Update(nil, i)
  40. }
  41. return ci
  42. }
  43. func (cs *ConnectionState) MarshalJSON() ([]byte, error) {
  44. return json.Marshal(m{
  45. "certificate": cs.peerCert,
  46. "initiator": cs.initiator,
  47. "message_counter": cs.messageCounter.Load(),
  48. })
  49. }
  50. func (cs *ConnectionState) Curve() cert.Curve {
  51. return cs.myCert.Curve()
  52. }
  53. func (cs *ConnectionState) Decrypt(l *slog.Logger, messageCounter uint64, packet []byte, nb []byte) ([]byte, error) {
  54. cs.decryptLock.Lock()
  55. result := cs.window.Check(l, messageCounter)
  56. cs.decryptLock.Unlock()
  57. if !result {
  58. return nil, ErrAlreadySeen
  59. }
  60. out, err := cs.dKey.DecryptDanger(packet[header.Len:header.Len], packet[:header.Len], packet[header.Len:], messageCounter, nb)
  61. if err != nil {
  62. return nil, err
  63. }
  64. cs.decryptLock.Lock()
  65. result = cs.window.Update(l, messageCounter)
  66. cs.decryptLock.Unlock()
  67. if !result {
  68. return nil, ErrAlreadySeen
  69. }
  70. return out, nil
  71. }
  72. func (cs *ConnectionState) VerifyRelay(l *slog.Logger, messageCounter uint64, packet []byte, nb []byte) error {
  73. cs.decryptLock.Lock()
  74. result := cs.window.Check(l, messageCounter)
  75. cs.decryptLock.Unlock()
  76. if !result {
  77. return ErrAlreadySeen
  78. }
  79. // The entire body is sent as AD, not encrypted.
  80. // The packet consists of a 16-byte parsed Nebula header, Associated Data-protected payload, and a trailing 16-byte AEAD signature value.
  81. // The packet is guaranteed to be at least 16 bytes at this point, b/c it got past the h.Parse() call above. If it's
  82. // otherwise malformed (meaning, there is no trailing 16 byte AEAD value), then this will result in at worst a 0-length slice
  83. // which will gracefully fail in the DecryptDanger call.
  84. signedPayload := packet[:len(packet)-cs.dKey.Overhead()]
  85. signatureValue := packet[len(packet)-cs.dKey.Overhead():]
  86. _, err := cs.dKey.DecryptDanger(nil, signedPayload, signatureValue, messageCounter, nb)
  87. if err != nil {
  88. return err
  89. }
  90. cs.decryptLock.Lock()
  91. result = cs.window.Update(l, messageCounter)
  92. cs.decryptLock.Unlock()
  93. if !result {
  94. return ErrAlreadySeen
  95. }
  96. return nil
  97. }