psk.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. package nebula
  2. import (
  3. "crypto/sha256"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "github.com/slackhq/nebula/config"
  8. "github.com/slackhq/nebula/iputil"
  9. "golang.org/x/crypto/hkdf"
  10. )
  11. var ErrNotAPskMode = errors.New("not a psk mode")
  12. var ErrKeyTooShort = errors.New("key is too short")
  13. var ErrNotEnoughPskKeys = errors.New("at least 1 key is required")
  14. // The minimum length that we accept for a user defined psk, the choice is arbitrary
  15. const MinPskLength = 8
  16. type PskMode int
  17. func (p PskMode) String() string {
  18. switch p {
  19. case PskNone:
  20. return "none"
  21. case PskTransitionalAccepting:
  22. return "transitional-accepting"
  23. case PskTransitionalSending:
  24. return "transitional-sending"
  25. case PskEnforced:
  26. return "enforced"
  27. }
  28. return "unknown"
  29. }
  30. func NewPskMode(m string) (PskMode, error) {
  31. switch m {
  32. case "none":
  33. return PskNone, nil
  34. case "transitional-accepting":
  35. return PskTransitionalAccepting, nil
  36. case "transitional-sending":
  37. return PskTransitionalSending, nil
  38. case "enforced":
  39. return PskEnforced, nil
  40. }
  41. return PskNone, ErrNotAPskMode
  42. }
  43. const (
  44. PskNone PskMode = 0
  45. PskTransitionalAccepting PskMode = 1
  46. PskTransitionalSending PskMode = 2
  47. PskEnforced PskMode = 3
  48. )
  49. type Psk struct {
  50. // pskMode sets how psk works, ignored, allowed for incoming, or enforced for all
  51. mode PskMode
  52. // Cache holds all pre-computed psk hkdfs
  53. // Handshakes iterate this directly
  54. Cache [][]byte
  55. // The key has already been extracted and is ready to be expanded for use
  56. // MakeFor does the final expand step mixing in the intended recipients vpn ip
  57. key []byte
  58. }
  59. // NewPskFromConfig is a helper for initial boot and config reloading.
  60. func NewPskFromConfig(c *config.C, myVpnIp iputil.VpnIp) (*Psk, error) {
  61. sMode := c.GetString("handshakes.psk.mode", "none")
  62. mode, err := NewPskMode(sMode)
  63. if err != nil {
  64. return nil, NewContextualError("Could not parse handshakes.psk.mode", m{"mode": mode}, err)
  65. }
  66. return NewPsk(
  67. mode,
  68. c.GetStringSlice("handshakes.psk.keys", nil),
  69. myVpnIp,
  70. )
  71. }
  72. // NewPsk creates a new Psk object and handles the caching of all accepted keys and preparation of the primary key
  73. func NewPsk(mode PskMode, keys []string, myVpnIp iputil.VpnIp) (*Psk, error) {
  74. psk := &Psk{
  75. mode: mode,
  76. }
  77. err := psk.preparePrimaryKey(keys)
  78. if err != nil {
  79. return nil, err
  80. }
  81. err = psk.cachePsks(myVpnIp, keys)
  82. if err != nil {
  83. return nil, err
  84. }
  85. return psk, nil
  86. }
  87. // MakeFor if we are in enforced mode, the final hkdf expand stage is done on the pre extracted primary key,
  88. // mixing in the intended recipients vpn ip and the result is returned.
  89. // If we are transitional or not using psks, an empty byte slice is returned
  90. func (p *Psk) MakeFor(vpnIp iputil.VpnIp) ([]byte, error) {
  91. if p.mode == PskNone || p.mode == PskTransitionalAccepting {
  92. return []byte{}, nil
  93. }
  94. hmacKey := make([]byte, sha256.Size)
  95. _, err := io.ReadFull(hkdf.Expand(sha256.New, p.key, vpnIp.ToIP()), hmacKey)
  96. if err != nil {
  97. return nil, err
  98. }
  99. return hmacKey, nil
  100. }
  101. // cachePsks generates all psks we accept and caches them to speed up handshaking
  102. func (p *Psk) cachePsks(myVpnIp iputil.VpnIp, keys []string) error {
  103. // If PskNone is set then we are using the nil byte array for a psk, we can return
  104. if p.mode == PskNone {
  105. p.Cache = [][]byte{nil}
  106. return nil
  107. }
  108. if len(keys) < 1 {
  109. return ErrNotEnoughPskKeys
  110. }
  111. p.Cache = [][]byte{}
  112. if p.mode == PskTransitionalAccepting || p.mode == PskTransitionalSending {
  113. // We are transitional, we accept empty psks
  114. p.Cache = append(p.Cache, nil)
  115. }
  116. // We are either PskAuto or PskTransitional, build all possibilities
  117. for i, rk := range keys {
  118. k, err := sha256KdfFromString(rk, myVpnIp)
  119. if err != nil {
  120. return fmt.Errorf("failed to generate key for position %v: %w", i, err)
  121. }
  122. p.Cache = append(p.Cache, k)
  123. }
  124. return nil
  125. }
  126. // preparePrimaryKey if we are in enforced mode, will do an hkdf extract on the first key to benefit
  127. // outgoing handshake performance, MakeFor does the final expand step
  128. func (p *Psk) preparePrimaryKey(keys []string) error {
  129. if p.mode == PskNone || p.mode == PskTransitionalAccepting {
  130. // If we aren't enforcing then there is nothing to prepare
  131. return nil
  132. }
  133. if len(keys) < 1 {
  134. return ErrNotEnoughPskKeys
  135. }
  136. p.key = hkdf.Extract(sha256.New, []byte(keys[0]), nil)
  137. return nil
  138. }
  139. // sha256KdfFromString generates a full hkdf
  140. func sha256KdfFromString(secret string, vpnIp iputil.VpnIp) ([]byte, error) {
  141. if len(secret) < MinPskLength {
  142. return nil, ErrKeyTooShort
  143. }
  144. hmacKey := make([]byte, sha256.Size)
  145. _, err := io.ReadFull(hkdf.New(sha256.New, []byte(secret), nil, vpnIp.ToIP()), hmacKey)
  146. if err != nil {
  147. return nil, err
  148. }
  149. return hmacKey, nil
  150. }