payload.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. KEMKey []byte // ML-KEM encapsulation key, only in subtypes that carry one
  20. }
  21. // Proto field numbers for NebulaHandshakeDetails
  22. const (
  23. fieldCert = 1 // bytes
  24. fieldInitiatorIndex = 2 // uint32
  25. fieldResponderIndex = 3 // uint32
  26. fieldTime = 5 // uint64
  27. fieldCertVersion = 8 // uint32
  28. fieldKEMKey = 9 // bytes
  29. )
  30. // MarshalPayload encodes a handshake payload in protobuf wire format compatible
  31. // with NebulaHandshake{Details: NebulaHandshakeDetails{...}}.
  32. // Returns out (which may be nil), with the marshalled Payload appended to it.
  33. func MarshalPayload(out []byte, p Payload) []byte {
  34. var details []byte
  35. if len(p.Cert) > 0 {
  36. details = protowire.AppendTag(details, fieldCert, protowire.BytesType)
  37. details = protowire.AppendBytes(details, p.Cert)
  38. }
  39. if p.InitiatorIndex != 0 {
  40. details = protowire.AppendTag(details, fieldInitiatorIndex, protowire.VarintType)
  41. details = protowire.AppendVarint(details, uint64(p.InitiatorIndex))
  42. }
  43. if p.ResponderIndex != 0 {
  44. details = protowire.AppendTag(details, fieldResponderIndex, protowire.VarintType)
  45. details = protowire.AppendVarint(details, uint64(p.ResponderIndex))
  46. }
  47. if p.Time != 0 {
  48. details = protowire.AppendTag(details, fieldTime, protowire.VarintType)
  49. details = protowire.AppendVarint(details, p.Time)
  50. }
  51. if p.CertVersion != 0 {
  52. details = protowire.AppendTag(details, fieldCertVersion, protowire.VarintType)
  53. details = protowire.AppendVarint(details, uint64(p.CertVersion))
  54. }
  55. if len(p.KEMKey) > 0 {
  56. details = protowire.AppendTag(details, fieldKEMKey, protowire.BytesType)
  57. details = protowire.AppendBytes(details, p.KEMKey)
  58. }
  59. out = protowire.AppendTag(out, 1, protowire.BytesType)
  60. out = protowire.AppendBytes(out, details)
  61. return out
  62. }
  63. // UnmarshalPayload decodes a protobuf-encoded NebulaHandshake message.
  64. func UnmarshalPayload(b []byte) (Payload, error) {
  65. var p Payload
  66. for len(b) > 0 {
  67. num, typ, n := protowire.ConsumeTag(b)
  68. if n < 0 {
  69. return p, errInvalidHandshakeMessage
  70. }
  71. b = b[n:]
  72. switch {
  73. case num == 1 && typ == protowire.BytesType:
  74. details, n := protowire.ConsumeBytes(b)
  75. if n < 0 {
  76. return p, errInvalidHandshakeMessage
  77. }
  78. b = b[n:]
  79. if err := unmarshalPayloadDetails(&p, details); err != nil {
  80. return p, err
  81. }
  82. default:
  83. n := protowire.ConsumeFieldValue(num, typ, b)
  84. if n < 0 {
  85. return p, errInvalidHandshakeMessage
  86. }
  87. b = b[n:]
  88. }
  89. }
  90. return p, nil
  91. }
  92. func unmarshalPayloadDetails(p *Payload, b []byte) error {
  93. for len(b) > 0 {
  94. num, typ, n := protowire.ConsumeTag(b)
  95. if n < 0 {
  96. return errInvalidHandshakeDetails
  97. }
  98. b = b[n:]
  99. // For known field numbers, reject any non-matching wire type as a
  100. // hard error rather than silently skipping. The caller will catch
  101. // missing-field cases downstream, but a wire-type mismatch on a tag
  102. // we know is a peer protocol violation worth flagging here.
  103. // Repeated occurrences of a singular field follow proto3 last-wins.
  104. switch num {
  105. case fieldCert:
  106. if typ != protowire.BytesType {
  107. return errInvalidHandshakeDetails
  108. }
  109. v, n := protowire.ConsumeBytes(b)
  110. if n < 0 {
  111. return errInvalidHandshakeDetails
  112. }
  113. p.Cert = append([]byte(nil), v...)
  114. b = b[n:]
  115. case fieldInitiatorIndex:
  116. if typ != protowire.VarintType {
  117. return errInvalidHandshakeDetails
  118. }
  119. v, n := protowire.ConsumeVarint(b)
  120. if n < 0 || v > math.MaxUint32 {
  121. return errInvalidHandshakeDetails
  122. }
  123. p.InitiatorIndex = uint32(v)
  124. b = b[n:]
  125. case fieldResponderIndex:
  126. if typ != protowire.VarintType {
  127. return errInvalidHandshakeDetails
  128. }
  129. v, n := protowire.ConsumeVarint(b)
  130. if n < 0 || v > math.MaxUint32 {
  131. return errInvalidHandshakeDetails
  132. }
  133. p.ResponderIndex = uint32(v)
  134. b = b[n:]
  135. case fieldTime:
  136. if typ != protowire.VarintType {
  137. return errInvalidHandshakeDetails
  138. }
  139. v, n := protowire.ConsumeVarint(b)
  140. if n < 0 {
  141. return errInvalidHandshakeDetails
  142. }
  143. p.Time = v
  144. b = b[n:]
  145. case fieldCertVersion:
  146. if typ != protowire.VarintType {
  147. return errInvalidHandshakeDetails
  148. }
  149. v, n := protowire.ConsumeVarint(b)
  150. if n < 0 || v > math.MaxUint32 {
  151. return errInvalidHandshakeDetails
  152. }
  153. p.CertVersion = uint32(v)
  154. b = b[n:]
  155. case fieldKEMKey:
  156. if typ != protowire.BytesType {
  157. return errInvalidHandshakeDetails
  158. }
  159. v, n := protowire.ConsumeBytes(b)
  160. if n < 0 {
  161. return errInvalidHandshakeDetails
  162. }
  163. p.KEMKey = append([]byte(nil), v...)
  164. b = b[n:]
  165. default:
  166. n := protowire.ConsumeFieldValue(num, typ, b)
  167. if n < 0 {
  168. return errInvalidHandshakeDetails
  169. }
  170. b = b[n:]
  171. }
  172. }
  173. return nil
  174. }