payload.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package handshake
  2. import (
  3. "errors"
  4. "math"
  5. "google.golang.org/protobuf/encoding/protowire"
  6. )
  7. var (
  8. errInvalidHandshakeMessage = errors.New("invalid handshake message")
  9. errInvalidHandshakeDetails = errors.New("invalid handshake details")
  10. )
  11. // Payload represents the decoded fields of a handshake message.
  12. // Wire format is protobuf-compatible with NebulaHandshake{Details: NebulaHandshakeDetails{...}}.
  13. type Payload struct {
  14. Cert []byte
  15. InitiatorIndex uint32
  16. ResponderIndex uint32
  17. Time uint64
  18. CertVersion uint32
  19. InitiatorMultiPort *PayloadMultiPortDetails
  20. ResponderMultiPort *PayloadMultiPortDetails
  21. }
  22. type PayloadMultiPortDetails struct {
  23. RxSupported bool
  24. TxSupported bool
  25. BasePort uint32
  26. TotalPorts uint32
  27. }
  28. // Proto field numbers for NebulaHandshakeDetails
  29. const (
  30. fieldCert = 1 // bytes
  31. fieldInitiatorIndex = 2 // uint32
  32. fieldResponderIndex = 3 // uint32
  33. fieldTime = 5 // uint64
  34. fieldCertVersion = 8 // uint32
  35. fieldInitiatorMultiPort = 6 // MultiPortDetails
  36. fieldResponderMultiPort = 7 // MultiPortDetails
  37. )
  38. // Proto field numbers for MultiPortDetails
  39. const (
  40. fieldMultiportRxSupported = 1 // bool
  41. fieldMultiportTxSupported = 2 // bool
  42. fieldMultiportBasePort = 3 // uint32
  43. fieldMultiportTotalPorts = 4 // uint32
  44. )
  45. // MarshalPayload encodes a handshake payload in protobuf wire format compatible
  46. // with NebulaHandshake{Details: NebulaHandshakeDetails{...}}.
  47. // Returns out (which may be nil), with the marshalled Payload appended to it.
  48. func MarshalPayload(out []byte, p Payload) []byte {
  49. var details []byte
  50. if len(p.Cert) > 0 {
  51. details = protowire.AppendTag(details, fieldCert, protowire.BytesType)
  52. details = protowire.AppendBytes(details, p.Cert)
  53. }
  54. if p.InitiatorIndex != 0 {
  55. details = protowire.AppendTag(details, fieldInitiatorIndex, protowire.VarintType)
  56. details = protowire.AppendVarint(details, uint64(p.InitiatorIndex))
  57. }
  58. if p.ResponderIndex != 0 {
  59. details = protowire.AppendTag(details, fieldResponderIndex, protowire.VarintType)
  60. details = protowire.AppendVarint(details, uint64(p.ResponderIndex))
  61. }
  62. if p.Time != 0 {
  63. details = protowire.AppendTag(details, fieldTime, protowire.VarintType)
  64. details = protowire.AppendVarint(details, p.Time)
  65. }
  66. if p.CertVersion != 0 {
  67. details = protowire.AppendTag(details, fieldCertVersion, protowire.VarintType)
  68. details = protowire.AppendVarint(details, uint64(p.CertVersion))
  69. }
  70. if p.InitiatorMultiPort != nil {
  71. details = protowire.AppendTag(details, fieldInitiatorMultiPort, protowire.BytesType)
  72. details = protowire.AppendVarint(details, uint64(p.InitiatorMultiPort.size()))
  73. details = p.InitiatorMultiPort.marshal(details)
  74. }
  75. if p.ResponderMultiPort != nil {
  76. details = protowire.AppendTag(details, fieldResponderMultiPort, protowire.BytesType)
  77. details = protowire.AppendVarint(details, uint64(p.ResponderMultiPort.size()))
  78. details = p.ResponderMultiPort.marshal(details)
  79. }
  80. out = protowire.AppendTag(out, 1, protowire.BytesType)
  81. out = protowire.AppendBytes(out, details)
  82. return out
  83. }
  84. func (p PayloadMultiPortDetails) marshal(details []byte) []byte {
  85. details = protowire.AppendTag(details, fieldMultiportRxSupported, protowire.VarintType)
  86. details = protowire.AppendVarint(details, protowire.EncodeBool(p.RxSupported))
  87. details = protowire.AppendTag(details, fieldMultiportTxSupported, protowire.VarintType)
  88. details = protowire.AppendVarint(details, protowire.EncodeBool(p.TxSupported))
  89. details = protowire.AppendTag(details, fieldMultiportBasePort, protowire.VarintType)
  90. details = protowire.AppendVarint(details, uint64(p.BasePort))
  91. details = protowire.AppendTag(details, fieldMultiportTotalPorts, protowire.VarintType)
  92. details = protowire.AppendVarint(details, uint64(p.TotalPorts))
  93. return details
  94. }
  95. func (p PayloadMultiPortDetails) size() int {
  96. return 4 + 2 + protowire.SizeVarint(uint64(p.BasePort)) + protowire.SizeVarint(uint64(p.TotalPorts))
  97. }
  98. // UnmarshalPayload decodes a protobuf-encoded NebulaHandshake message.
  99. func UnmarshalPayload(b []byte) (Payload, error) {
  100. var p Payload
  101. for len(b) > 0 {
  102. num, typ, n := protowire.ConsumeTag(b)
  103. if n < 0 {
  104. return p, errInvalidHandshakeMessage
  105. }
  106. b = b[n:]
  107. switch {
  108. case num == 1 && typ == protowire.BytesType:
  109. details, n := protowire.ConsumeBytes(b)
  110. if n < 0 {
  111. return p, errInvalidHandshakeMessage
  112. }
  113. b = b[n:]
  114. if err := unmarshalPayloadDetails(&p, details); err != nil {
  115. return p, err
  116. }
  117. default:
  118. n := protowire.ConsumeFieldValue(num, typ, b)
  119. if n < 0 {
  120. return p, errInvalidHandshakeMessage
  121. }
  122. b = b[n:]
  123. }
  124. }
  125. return p, nil
  126. }
  127. func unmarshalPayloadDetails(p *Payload, b []byte) error {
  128. for len(b) > 0 {
  129. num, typ, n := protowire.ConsumeTag(b)
  130. if n < 0 {
  131. return errInvalidHandshakeDetails
  132. }
  133. b = b[n:]
  134. // For known field numbers, reject any non-matching wire type as a
  135. // hard error rather than silently skipping. The caller will catch
  136. // missing-field cases downstream, but a wire-type mismatch on a tag
  137. // we know is a peer protocol violation worth flagging here.
  138. // Repeated occurrences of a singular field follow proto3 last-wins.
  139. switch num {
  140. case fieldCert:
  141. if typ != protowire.BytesType {
  142. return errInvalidHandshakeDetails
  143. }
  144. v, n := protowire.ConsumeBytes(b)
  145. if n < 0 {
  146. return errInvalidHandshakeDetails
  147. }
  148. p.Cert = append([]byte(nil), v...)
  149. b = b[n:]
  150. case fieldInitiatorIndex:
  151. if typ != protowire.VarintType {
  152. return errInvalidHandshakeDetails
  153. }
  154. v, n := protowire.ConsumeVarint(b)
  155. if n < 0 || v > math.MaxUint32 {
  156. return errInvalidHandshakeDetails
  157. }
  158. p.InitiatorIndex = uint32(v)
  159. b = b[n:]
  160. case fieldResponderIndex:
  161. if typ != protowire.VarintType {
  162. return errInvalidHandshakeDetails
  163. }
  164. v, n := protowire.ConsumeVarint(b)
  165. if n < 0 || v > math.MaxUint32 {
  166. return errInvalidHandshakeDetails
  167. }
  168. p.ResponderIndex = uint32(v)
  169. b = b[n:]
  170. case fieldTime:
  171. if typ != protowire.VarintType {
  172. return errInvalidHandshakeDetails
  173. }
  174. v, n := protowire.ConsumeVarint(b)
  175. if n < 0 {
  176. return errInvalidHandshakeDetails
  177. }
  178. p.Time = v
  179. b = b[n:]
  180. case fieldCertVersion:
  181. if typ != protowire.VarintType {
  182. return errInvalidHandshakeDetails
  183. }
  184. v, n := protowire.ConsumeVarint(b)
  185. if n < 0 || v > math.MaxUint32 {
  186. return errInvalidHandshakeDetails
  187. }
  188. p.CertVersion = uint32(v)
  189. b = b[n:]
  190. case fieldInitiatorMultiPort:
  191. if typ != protowire.BytesType {
  192. return errInvalidHandshakeDetails
  193. }
  194. d, n := protowire.ConsumeBytes(b)
  195. if n < 0 {
  196. return errInvalidHandshakeMessage
  197. }
  198. b = b[n:]
  199. p.InitiatorMultiPort = new(PayloadMultiPortDetails)
  200. if err := unmarshalPayloadMultiPortDetails(p.InitiatorMultiPort, d); err != nil {
  201. return err
  202. }
  203. case fieldResponderMultiPort:
  204. if typ != protowire.BytesType {
  205. return errInvalidHandshakeDetails
  206. }
  207. d, n := protowire.ConsumeBytes(b)
  208. if n < 0 {
  209. return errInvalidHandshakeMessage
  210. }
  211. b = b[n:]
  212. p.ResponderMultiPort = new(PayloadMultiPortDetails)
  213. if err := unmarshalPayloadMultiPortDetails(p.ResponderMultiPort, d); err != nil {
  214. return err
  215. }
  216. default:
  217. n := protowire.ConsumeFieldValue(num, typ, b)
  218. if n < 0 {
  219. return errInvalidHandshakeDetails
  220. }
  221. b = b[n:]
  222. }
  223. }
  224. return nil
  225. }
  226. func unmarshalPayloadMultiPortDetails(p *PayloadMultiPortDetails, b []byte) error {
  227. for len(b) > 0 {
  228. num, typ, n := protowire.ConsumeTag(b)
  229. if n < 0 {
  230. return errInvalidHandshakeDetails
  231. }
  232. b = b[n:]
  233. // For known field numbers, reject any non-matching wire type as a
  234. // hard error rather than silently skipping. The caller will catch
  235. // missing-field cases downstream, but a wire-type mismatch on a tag
  236. // we know is a peer protocol violation worth flagging here.
  237. // Repeated occurrences of a singular field follow proto3 last-wins.
  238. switch num {
  239. case fieldMultiportRxSupported:
  240. if typ != protowire.VarintType {
  241. return errInvalidHandshakeDetails
  242. }
  243. v, n := protowire.ConsumeVarint(b)
  244. if n < 0 || v > math.MaxUint32 {
  245. return errInvalidHandshakeDetails
  246. }
  247. p.RxSupported = protowire.DecodeBool(v)
  248. b = b[n:]
  249. case fieldMultiportTxSupported:
  250. if typ != protowire.VarintType {
  251. return errInvalidHandshakeDetails
  252. }
  253. v, n := protowire.ConsumeVarint(b)
  254. if n < 0 || v > math.MaxUint32 {
  255. return errInvalidHandshakeDetails
  256. }
  257. p.TxSupported = protowire.DecodeBool(v)
  258. b = b[n:]
  259. case fieldMultiportBasePort:
  260. if typ != protowire.VarintType {
  261. return errInvalidHandshakeDetails
  262. }
  263. v, n := protowire.ConsumeVarint(b)
  264. if n < 0 || v > math.MaxUint32 {
  265. return errInvalidHandshakeDetails
  266. }
  267. p.BasePort = uint32(v)
  268. b = b[n:]
  269. case fieldMultiportTotalPorts:
  270. if typ != protowire.VarintType {
  271. return errInvalidHandshakeDetails
  272. }
  273. v, n := protowire.ConsumeVarint(b)
  274. if n < 0 || v > math.MaxUint32 {
  275. return errInvalidHandshakeDetails
  276. }
  277. p.TotalPorts = uint32(v)
  278. b = b[n:]
  279. default:
  280. n := protowire.ConsumeFieldValue(num, typ, b)
  281. if n < 0 {
  282. return errInvalidHandshakeDetails
  283. }
  284. b = b[n:]
  285. }
  286. }
  287. return nil
  288. }