wire.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. package ike
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. )
  8. const (
  9. ikeHeaderLength = 28
  10. ikeMajorVersion = 2
  11. ikeMinorVersion = 0
  12. exchangeIKEInit = 34
  13. exchangeIKEAuth = 35
  14. exchangeInformational = 37
  15. flagInitiator = 0x08
  16. flagResponse = 0x20
  17. payloadNone = 0
  18. payloadSA = 33
  19. payloadKE = 34
  20. payloadIDi = 35
  21. payloadIDr = 36
  22. payloadCert = 37
  23. payloadAuth = 39
  24. payloadNonce = 40
  25. payloadNotify = 41
  26. payloadTSi = 44
  27. payloadTSr = 45
  28. payloadEncrypted = 46
  29. payloadCP = 47
  30. payloadEAP = 48
  31. protocolIKE = 1
  32. protocolESP = 3
  33. transformEncryption = 1
  34. transformPRF = 2
  35. transformIntegrity = 3
  36. transformDH = 4
  37. transformESN = 5
  38. encryptionAESCBC = 12
  39. prfHMACSHA1 = 2
  40. prfHMACSHA256 = 5
  41. integrityHMACSHA1_96 = 2
  42. integrityHMACSHA256_128 = 12
  43. dhMODP1024 = 2
  44. dhMODP2048 = 14
  45. transformAttributeKeyLen = 14
  46. notifyNATSource = 16388
  47. notifyNATDestination = 16389
  48. notifyEAPOnlyAuth = 16417
  49. notifyInvalidKE = 17
  50. notifyNoProposal = 14
  51. )
  52. var (
  53. errMalformedPacket = errors.New("ike: malformed packet")
  54. errUnexpectedPacket = errors.New("ike: unexpected packet")
  55. errUnsupportedSuite = errors.New("ike: unsupported negotiated suite")
  56. errIntegrityMismatch = errors.New("ike: encrypted payload integrity mismatch")
  57. )
  58. type ikeHeader struct {
  59. InitiatorSPI [8]byte
  60. ResponderSPI [8]byte
  61. NextPayload uint8
  62. Version uint8
  63. Exchange uint8
  64. Flags uint8
  65. MessageID uint32
  66. Length uint32
  67. }
  68. func (header ikeHeader) marshal(body []byte) []byte {
  69. packet := make([]byte, ikeHeaderLength+len(body))
  70. copy(packet[0:8], header.InitiatorSPI[:])
  71. copy(packet[8:16], header.ResponderSPI[:])
  72. packet[16] = header.NextPayload
  73. if header.Version == 0 {
  74. header.Version = ikeMajorVersion<<4 | ikeMinorVersion
  75. }
  76. packet[17] = header.Version
  77. packet[18] = header.Exchange
  78. packet[19] = header.Flags
  79. binary.BigEndian.PutUint32(packet[20:24], header.MessageID)
  80. binary.BigEndian.PutUint32(packet[24:28], uint32(len(packet)))
  81. copy(packet[28:], body)
  82. return packet
  83. }
  84. func parseIKEPacket(packet []byte) (ikeHeader, []byte, error) {
  85. if len(packet) < ikeHeaderLength {
  86. return ikeHeader{}, nil, fmt.Errorf("%w: header is truncated", errMalformedPacket)
  87. }
  88. var header ikeHeader
  89. copy(header.InitiatorSPI[:], packet[0:8])
  90. copy(header.ResponderSPI[:], packet[8:16])
  91. header.NextPayload = packet[16]
  92. header.Version = packet[17]
  93. header.Exchange = packet[18]
  94. header.Flags = packet[19]
  95. header.MessageID = binary.BigEndian.Uint32(packet[20:24])
  96. header.Length = binary.BigEndian.Uint32(packet[24:28])
  97. if header.Version>>4 != ikeMajorVersion {
  98. return ikeHeader{}, nil, fmt.Errorf("%w: unsupported IKE major version %d", errMalformedPacket, header.Version>>4)
  99. }
  100. if header.Length < ikeHeaderLength || uint64(header.Length) != uint64(len(packet)) {
  101. return ikeHeader{}, nil, fmt.Errorf("%w: encoded length %d does not match datagram length %d", errMalformedPacket, header.Length, len(packet))
  102. }
  103. return header, packet[ikeHeaderLength:], nil
  104. }
  105. type payload struct {
  106. Type uint8
  107. Critical bool
  108. Body []byte
  109. }
  110. func marshalPayloadChain(payloads []payload) (uint8, []byte, error) {
  111. if len(payloads) == 0 {
  112. return payloadNone, nil, nil
  113. }
  114. var output bytes.Buffer
  115. for index, item := range payloads {
  116. if item.Type == payloadNone || item.Type == payloadEncrypted {
  117. return 0, nil, fmt.Errorf("ike: invalid ordinary payload type %d", item.Type)
  118. }
  119. next := uint8(payloadNone)
  120. if index+1 < len(payloads) {
  121. next = payloads[index+1].Type
  122. }
  123. length := 4 + len(item.Body)
  124. if length > 65535 {
  125. return 0, nil, errors.New("ike: payload exceeds 65535 bytes")
  126. }
  127. output.WriteByte(next)
  128. if item.Critical {
  129. output.WriteByte(0x80)
  130. } else {
  131. output.WriteByte(0)
  132. }
  133. var encodedLength [2]byte
  134. binary.BigEndian.PutUint16(encodedLength[:], uint16(length))
  135. output.Write(encodedLength[:])
  136. output.Write(item.Body)
  137. }
  138. return payloads[0].Type, output.Bytes(), nil
  139. }
  140. func parsePayloadChain(first uint8, encoded []byte) ([]payload, error) {
  141. var result []payload
  142. next := first
  143. offset := 0
  144. for next != payloadNone {
  145. if len(result) >= 64 {
  146. return nil, fmt.Errorf("%w: too many chained payloads", errMalformedPacket)
  147. }
  148. if offset+4 > len(encoded) {
  149. return nil, fmt.Errorf("%w: payload header is truncated", errMalformedPacket)
  150. }
  151. following := encoded[offset]
  152. flags := encoded[offset+1]
  153. length := int(binary.BigEndian.Uint16(encoded[offset+2 : offset+4]))
  154. if length < 4 || offset+length > len(encoded) {
  155. return nil, fmt.Errorf("%w: payload type %d has invalid length %d", errMalformedPacket, next, length)
  156. }
  157. body := append([]byte(nil), encoded[offset+4:offset+length]...)
  158. result = append(result, payload{Type: next, Critical: flags&0x80 != 0, Body: body})
  159. offset += length
  160. next = following
  161. }
  162. if offset != len(encoded) {
  163. return nil, fmt.Errorf("%w: %d trailing payload bytes", errMalformedPacket, len(encoded)-offset)
  164. }
  165. return result, nil
  166. }
  167. func payloadsOfType(payloads []payload, kind uint8) []payload {
  168. var matches []payload
  169. for _, item := range payloads {
  170. if item.Type == kind {
  171. matches = append(matches, item)
  172. }
  173. }
  174. return matches
  175. }
  176. func onePayload(payloads []payload, kind uint8) (payload, error) {
  177. matches := payloadsOfType(payloads, kind)
  178. if len(matches) != 1 {
  179. return payload{}, fmt.Errorf("%w: expected one payload type %d, got %d", errUnexpectedPacket, kind, len(matches))
  180. }
  181. return matches[0], nil
  182. }
  183. type transform struct {
  184. Type uint8
  185. ID uint16
  186. KeyLength int
  187. }
  188. type proposal struct {
  189. Number uint8
  190. Protocol uint8
  191. SPI []byte
  192. Transforms []transform
  193. }
  194. func marshalProposals(proposals []proposal) ([]byte, error) {
  195. var output bytes.Buffer
  196. for proposalIndex, item := range proposals {
  197. if len(item.SPI) > 255 || len(item.Transforms) > 255 {
  198. return nil, errors.New("ike: proposal has too many bytes or transforms")
  199. }
  200. var transforms bytes.Buffer
  201. for transformIndex, candidate := range item.Transforms {
  202. var attributes []byte
  203. if candidate.KeyLength > 0 {
  204. attributes = make([]byte, 4)
  205. binary.BigEndian.PutUint16(attributes[0:2], 0x8000|transformAttributeKeyLen)
  206. binary.BigEndian.PutUint16(attributes[2:4], uint16(candidate.KeyLength))
  207. }
  208. length := 8 + len(attributes)
  209. if transformIndex+1 < len(item.Transforms) {
  210. transforms.WriteByte(3)
  211. } else {
  212. transforms.WriteByte(0)
  213. }
  214. transforms.WriteByte(0)
  215. var header [6]byte
  216. binary.BigEndian.PutUint16(header[0:2], uint16(length))
  217. header[2] = candidate.Type
  218. header[3] = 0
  219. binary.BigEndian.PutUint16(header[4:6], candidate.ID)
  220. transforms.Write(header[:])
  221. transforms.Write(attributes)
  222. }
  223. length := 8 + len(item.SPI) + transforms.Len()
  224. if proposalIndex+1 < len(proposals) {
  225. output.WriteByte(2)
  226. } else {
  227. output.WriteByte(0)
  228. }
  229. output.WriteByte(0)
  230. var header [6]byte
  231. binary.BigEndian.PutUint16(header[0:2], uint16(length))
  232. header[2] = item.Number
  233. header[3] = item.Protocol
  234. header[4] = uint8(len(item.SPI))
  235. header[5] = uint8(len(item.Transforms))
  236. output.Write(header[:])
  237. output.Write(item.SPI)
  238. output.Write(transforms.Bytes())
  239. }
  240. return output.Bytes(), nil
  241. }
  242. func parseProposals(encoded []byte) ([]proposal, error) {
  243. var result []proposal
  244. offset := 0
  245. for {
  246. if offset == len(encoded) {
  247. break
  248. }
  249. if len(result) >= 16 || offset+8 > len(encoded) {
  250. return nil, fmt.Errorf("%w: invalid SA proposal header", errMalformedPacket)
  251. }
  252. last := encoded[offset]
  253. length := int(binary.BigEndian.Uint16(encoded[offset+2 : offset+4]))
  254. spiSize := int(encoded[offset+6])
  255. transformCount := int(encoded[offset+7])
  256. if length < 8+spiSize || offset+length > len(encoded) {
  257. return nil, fmt.Errorf("%w: invalid SA proposal length", errMalformedPacket)
  258. }
  259. item := proposal{
  260. Number: encoded[offset+4],
  261. Protocol: encoded[offset+5],
  262. SPI: append([]byte(nil), encoded[offset+8:offset+8+spiSize]...),
  263. }
  264. transformOffset := offset + 8 + spiSize
  265. proposalEnd := offset + length
  266. for transformOffset < proposalEnd {
  267. if len(item.Transforms) >= 32 || transformOffset+8 > proposalEnd {
  268. return nil, fmt.Errorf("%w: invalid transform header", errMalformedPacket)
  269. }
  270. transformLength := int(binary.BigEndian.Uint16(encoded[transformOffset+2 : transformOffset+4]))
  271. if transformLength < 8 || transformOffset+transformLength > proposalEnd {
  272. return nil, fmt.Errorf("%w: invalid transform length", errMalformedPacket)
  273. }
  274. transformEnd := transformOffset + transformLength
  275. if transformEnd < proposalEnd && encoded[transformOffset] != 3 {
  276. return nil, fmt.Errorf("%w: non-final transform has invalid chaining marker", errMalformedPacket)
  277. }
  278. if transformEnd == proposalEnd && encoded[transformOffset] != 0 {
  279. return nil, fmt.Errorf("%w: final transform has invalid chaining marker", errMalformedPacket)
  280. }
  281. candidate := transform{
  282. Type: encoded[transformOffset+4],
  283. ID: binary.BigEndian.Uint16(encoded[transformOffset+6 : transformOffset+8]),
  284. }
  285. attributes := encoded[transformOffset+8 : transformOffset+transformLength]
  286. for len(attributes) > 0 {
  287. if len(attributes) < 4 {
  288. return nil, fmt.Errorf("%w: truncated transform attribute", errMalformedPacket)
  289. }
  290. attributeType := binary.BigEndian.Uint16(attributes[0:2])
  291. if attributeType&0x8000 != 0 {
  292. if attributeType&0x7fff == transformAttributeKeyLen {
  293. candidate.KeyLength = int(binary.BigEndian.Uint16(attributes[2:4]))
  294. }
  295. attributes = attributes[4:]
  296. continue
  297. }
  298. attributeLength := int(binary.BigEndian.Uint16(attributes[2:4]))
  299. if attributeLength < 0 || 4+attributeLength > len(attributes) {
  300. return nil, fmt.Errorf("%w: invalid transform TLV attribute", errMalformedPacket)
  301. }
  302. if attributeType == transformAttributeKeyLen && attributeLength == 2 {
  303. candidate.KeyLength = int(binary.BigEndian.Uint16(attributes[4:6]))
  304. }
  305. attributes = attributes[4+attributeLength:]
  306. }
  307. item.Transforms = append(item.Transforms, candidate)
  308. transformOffset += transformLength
  309. }
  310. if transformOffset != proposalEnd || len(item.Transforms) != transformCount {
  311. return nil, fmt.Errorf("%w: transform count mismatch", errMalformedPacket)
  312. }
  313. result = append(result, item)
  314. offset = proposalEnd
  315. if last == 0 {
  316. if offset != len(encoded) {
  317. return nil, fmt.Errorf("%w: bytes follow last proposal", errMalformedPacket)
  318. }
  319. break
  320. }
  321. if last != 2 {
  322. return nil, fmt.Errorf("%w: invalid proposal chaining marker %d", errMalformedPacket, last)
  323. }
  324. }
  325. if len(result) == 0 {
  326. return nil, fmt.Errorf("%w: empty SA payload", errMalformedPacket)
  327. }
  328. return result, nil
  329. }
  330. type negotiatedSuite struct {
  331. EncryptionID uint16
  332. EncryptionBits int
  333. PRFID uint16
  334. IntegrityID uint16
  335. DHID uint16
  336. }
  337. func parseIKESuite(item proposal) (negotiatedSuite, error) {
  338. if item.Protocol != protocolIKE || len(item.SPI) != 0 {
  339. return negotiatedSuite{}, fmt.Errorf("%w: responder selected a non-IKE proposal", errUnsupportedSuite)
  340. }
  341. var suite negotiatedSuite
  342. seen := make(map[uint8]bool)
  343. for _, candidate := range item.Transforms {
  344. if seen[candidate.Type] {
  345. return negotiatedSuite{}, fmt.Errorf("%w: duplicate transform type %d", errUnsupportedSuite, candidate.Type)
  346. }
  347. seen[candidate.Type] = true
  348. switch candidate.Type {
  349. case transformEncryption:
  350. suite.EncryptionID = candidate.ID
  351. suite.EncryptionBits = candidate.KeyLength
  352. case transformPRF:
  353. suite.PRFID = candidate.ID
  354. case transformIntegrity:
  355. suite.IntegrityID = candidate.ID
  356. case transformDH:
  357. suite.DHID = candidate.ID
  358. default:
  359. return negotiatedSuite{}, fmt.Errorf("%w: IKE transform type %d", errUnsupportedSuite, candidate.Type)
  360. }
  361. }
  362. if suite.EncryptionID != encryptionAESCBC || (suite.EncryptionBits != 128 && suite.EncryptionBits != 256) {
  363. return negotiatedSuite{}, fmt.Errorf("%w: encryption id=%d bits=%d", errUnsupportedSuite, suite.EncryptionID, suite.EncryptionBits)
  364. }
  365. if suite.PRFID != prfHMACSHA1 && suite.PRFID != prfHMACSHA256 {
  366. return negotiatedSuite{}, fmt.Errorf("%w: PRF id=%d", errUnsupportedSuite, suite.PRFID)
  367. }
  368. if suite.IntegrityID != integrityHMACSHA1_96 && suite.IntegrityID != integrityHMACSHA256_128 {
  369. return negotiatedSuite{}, fmt.Errorf("%w: integrity id=%d", errUnsupportedSuite, suite.IntegrityID)
  370. }
  371. if suite.DHID != dhMODP1024 && suite.DHID != dhMODP2048 {
  372. return negotiatedSuite{}, fmt.Errorf("%w: DH id=%d", errUnsupportedSuite, suite.DHID)
  373. }
  374. return suite, nil
  375. }
  376. func makeNotify(notifyType uint16, data []byte) payload {
  377. body := make([]byte, 4+len(data))
  378. body[0] = 0
  379. body[1] = 0
  380. binary.BigEndian.PutUint16(body[2:4], notifyType)
  381. copy(body[4:], data)
  382. return payload{Type: payloadNotify, Body: body}
  383. }
  384. func parseNotify(item payload) (uint16, []byte, error) {
  385. if item.Type != payloadNotify || len(item.Body) < 4 {
  386. return 0, nil, fmt.Errorf("%w: invalid notify payload", errMalformedPacket)
  387. }
  388. spiSize := int(item.Body[1])
  389. if 4+spiSize > len(item.Body) {
  390. return 0, nil, fmt.Errorf("%w: truncated notify SPI", errMalformedPacket)
  391. }
  392. return binary.BigEndian.Uint16(item.Body[2:4]), append([]byte(nil), item.Body[4+spiSize:]...), nil
  393. }