obfuscatedSshConn.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. * Copyright (c) 2015, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package common
  20. import (
  21. "bytes"
  22. "encoding/binary"
  23. "errors"
  24. "io"
  25. "net"
  26. )
  27. const (
  28. SSH_MAX_SERVER_LINE_LENGTH = 1024
  29. SSH_PACKET_PREFIX_LENGTH = 5 // uint32 + byte
  30. SSH_MAX_PACKET_LENGTH = 256 * 1024 // OpenSSH max packet length
  31. SSH_MSG_NEWKEYS = 21
  32. SSH_MAX_PADDING_LENGTH = 255 // RFC 4253 sec. 6
  33. SSH_PADDING_MULTIPLE = 16 // Default cipher block size
  34. )
  35. // ObfuscatedSshConn wraps a Conn and applies the obfuscated SSH protocol
  36. // to the traffic on the connection:
  37. // https://github.com/brl/obfuscated-openssh/blob/master/README.obfuscation
  38. //
  39. // ObfuscatedSshConn is used to add obfuscation to golang's stock ssh
  40. // client and server without modification to that standard library code.
  41. // The underlying connection must be used for SSH traffic. This code
  42. // injects the obfuscated seed message, applies obfuscated stream cipher
  43. // transformations, and performs minimal parsing of the SSH protocol to
  44. // determine when to stop obfuscation (after the first SSH_MSG_NEWKEYS is
  45. // sent and received).
  46. //
  47. // WARNING: doesn't fully conform to net.Conn concurrency semantics: there's
  48. // no synchronization of access to the read/writeBuffers, so concurrent
  49. // calls to one of Read or Write will result in undefined behavior.
  50. //
  51. type ObfuscatedSshConn struct {
  52. net.Conn
  53. mode ObfuscatedSshConnMode
  54. obfuscator *Obfuscator
  55. readDeobfuscate func([]byte)
  56. writeObfuscate func([]byte)
  57. readState ObfuscatedSshReadState
  58. writeState ObfuscatedSshWriteState
  59. readBuffer *bytes.Buffer
  60. writeBuffer *bytes.Buffer
  61. transformBuffer *bytes.Buffer
  62. }
  63. type ObfuscatedSshConnMode int
  64. const (
  65. OBFUSCATION_CONN_MODE_CLIENT = iota
  66. OBFUSCATION_CONN_MODE_SERVER
  67. )
  68. type ObfuscatedSshReadState int
  69. const (
  70. OBFUSCATION_READ_STATE_IDENTIFICATION_LINES = iota
  71. OBFUSCATION_READ_STATE_KEX_PACKETS
  72. OBFUSCATION_READ_STATE_FLUSH
  73. OBFUSCATION_READ_STATE_FINISHED
  74. )
  75. type ObfuscatedSshWriteState int
  76. const (
  77. OBFUSCATION_WRITE_STATE_CLIENT_SEND_SEED_MESSAGE = iota
  78. OBFUSCATION_WRITE_STATE_SERVER_SEND_IDENTIFICATION_LINE_PADDING
  79. OBFUSCATION_WRITE_STATE_IDENTIFICATION_LINE
  80. OBFUSCATION_WRITE_STATE_KEX_PACKETS
  81. OBFUSCATION_WRITE_STATE_FINISHED
  82. )
  83. // NewObfuscatedSshConn creates a new ObfuscatedSshConn.
  84. // The underlying conn must be used for SSH traffic and must have
  85. // transferred no traffic.
  86. //
  87. // In client mode, NewObfuscatedSshConn does not block or initiate network
  88. // I/O. The obfuscation seed message is sent when Write() is first called.
  89. //
  90. // In server mode, NewObfuscatedSshConn cannot completely initialize itself
  91. // without the seed message from the client to derive obfuscation keys. So
  92. // NewObfuscatedSshConn blocks on reading the client seed message from the
  93. // underlying conn.
  94. //
  95. func NewObfuscatedSshConn(
  96. mode ObfuscatedSshConnMode,
  97. conn net.Conn,
  98. obfuscationKeyword string) (*ObfuscatedSshConn, error) {
  99. var err error
  100. var obfuscator *Obfuscator
  101. var readDeobfuscate, writeObfuscate func([]byte)
  102. var writeState ObfuscatedSshWriteState
  103. if mode == OBFUSCATION_CONN_MODE_CLIENT {
  104. obfuscator, err = NewClientObfuscator(&ObfuscatorConfig{Keyword: obfuscationKeyword})
  105. if err != nil {
  106. return nil, ContextError(err)
  107. }
  108. readDeobfuscate = obfuscator.ObfuscateServerToClient
  109. writeObfuscate = obfuscator.ObfuscateClientToServer
  110. writeState = OBFUSCATION_WRITE_STATE_CLIENT_SEND_SEED_MESSAGE
  111. } else {
  112. // NewServerObfuscator reads a seed message from conn
  113. obfuscator, err = NewServerObfuscator(
  114. conn, &ObfuscatorConfig{Keyword: obfuscationKeyword})
  115. if err != nil {
  116. // TODO: readForver() equivalent
  117. return nil, ContextError(err)
  118. }
  119. readDeobfuscate = obfuscator.ObfuscateClientToServer
  120. writeObfuscate = obfuscator.ObfuscateServerToClient
  121. writeState = OBFUSCATION_WRITE_STATE_SERVER_SEND_IDENTIFICATION_LINE_PADDING
  122. }
  123. return &ObfuscatedSshConn{
  124. Conn: conn,
  125. mode: mode,
  126. obfuscator: obfuscator,
  127. readDeobfuscate: readDeobfuscate,
  128. writeObfuscate: writeObfuscate,
  129. readState: OBFUSCATION_READ_STATE_IDENTIFICATION_LINES,
  130. writeState: writeState,
  131. readBuffer: new(bytes.Buffer),
  132. writeBuffer: new(bytes.Buffer),
  133. transformBuffer: new(bytes.Buffer),
  134. }, nil
  135. }
  136. // Read wraps standard Read, transparently applying the obfuscation
  137. // transformations.
  138. func (conn *ObfuscatedSshConn) Read(buffer []byte) (int, error) {
  139. if conn.readState == OBFUSCATION_READ_STATE_FINISHED {
  140. return conn.Conn.Read(buffer)
  141. }
  142. n, err := conn.readAndTransform(buffer)
  143. if err != nil {
  144. err = ContextError(err)
  145. }
  146. return n, err
  147. }
  148. // Write wraps standard Write, transparently applying the obfuscation
  149. // transformations.
  150. func (conn *ObfuscatedSshConn) Write(buffer []byte) (int, error) {
  151. if conn.writeState == OBFUSCATION_WRITE_STATE_FINISHED {
  152. return conn.Conn.Write(buffer)
  153. }
  154. err := conn.transformAndWrite(buffer)
  155. if err != nil {
  156. return 0, ContextError(err)
  157. }
  158. // Reports that we wrote all the bytes
  159. // (although we may have buffered some or all)
  160. return len(buffer), nil
  161. }
  162. // readAndTransform reads and transforms the downstream bytes stream
  163. // while in an obfucation state. It parses the stream of bytes read
  164. // looking for the first SSH_MSG_NEWKEYS packet sent from the peer,
  165. // after which obfuscation is turned off. Since readAndTransform may
  166. // read in more bytes that the higher-level conn.Read() can consume,
  167. // read bytes are buffered and may be returned in subsequent calls.
  168. //
  169. // readAndTransform also implements a workaround for issues with
  170. // ssh/transport.go exchangeVersions/readVersion and Psiphon's openssh
  171. // server.
  172. //
  173. // Psiphon's server sends extra lines before the version line, as
  174. // permitted by http://www.ietf.org/rfc/rfc4253.txt sec 4.2:
  175. // The server MAY send other lines of data before sending the
  176. // version string. [...] Clients MUST be able to process such lines.
  177. //
  178. // A comment in exchangeVersions explains that the golang code doesn't
  179. // support this:
  180. // Contrary to the RFC, we do not ignore lines that don't
  181. // start with "SSH-2.0-" to make the library usable with
  182. // nonconforming servers.
  183. //
  184. // In addition, Psiphon's server sends up to 512 characters per extra
  185. // line. It's not clear that the 255 max string size in sec 4.2 refers
  186. // to the extra lines as well, but in any case golang's code only
  187. // supports 255 character lines.
  188. //
  189. // State OBFUSCATION_READ_STATE_IDENTIFICATION_LINES: in this
  190. // state, extra lines are read and discarded. Once the peer's
  191. // identification string line is read, it is buffered and returned
  192. // as per the requested read buffer size.
  193. //
  194. // State OBFUSCATION_READ_STATE_KEX_PACKETS: reads, deobfuscates,
  195. // and buffers full SSH packets, checking for SSH_MSG_NEWKEYS. Packet
  196. // data is returned as per the requested read buffer size.
  197. //
  198. // State OBFUSCATION_READ_STATE_FLUSH: after SSH_MSG_NEWKEYS, no more
  199. // packets are read by this function, but bytes from the SSH_MSG_NEWKEYS
  200. // packet may need to be buffered due to partial reading.
  201. func (conn *ObfuscatedSshConn) readAndTransform(buffer []byte) (int, error) {
  202. nextState := conn.readState
  203. switch conn.readState {
  204. case OBFUSCATION_READ_STATE_IDENTIFICATION_LINES:
  205. // TODO: only client should accept multiple lines?
  206. if conn.readBuffer.Len() == 0 {
  207. for {
  208. err := readSshIdentificationLine(
  209. conn.Conn, conn.readDeobfuscate, conn.readBuffer)
  210. if err != nil {
  211. return 0, ContextError(err)
  212. }
  213. if bytes.HasPrefix(conn.readBuffer.Bytes(), []byte("SSH-")) {
  214. break
  215. }
  216. // Discard extra line
  217. conn.readBuffer.Truncate(0)
  218. }
  219. }
  220. nextState = OBFUSCATION_READ_STATE_KEX_PACKETS
  221. case OBFUSCATION_READ_STATE_KEX_PACKETS:
  222. if conn.readBuffer.Len() == 0 {
  223. isMsgNewKeys, err := readSshPacket(
  224. conn.Conn, conn.readDeobfuscate, conn.readBuffer)
  225. if err != nil {
  226. return 0, ContextError(err)
  227. }
  228. if isMsgNewKeys {
  229. nextState = OBFUSCATION_READ_STATE_FLUSH
  230. }
  231. }
  232. case OBFUSCATION_READ_STATE_FLUSH:
  233. nextState = OBFUSCATION_READ_STATE_FINISHED
  234. case OBFUSCATION_READ_STATE_FINISHED:
  235. return 0, ContextError(errors.New("invalid read state"))
  236. }
  237. n, err := conn.readBuffer.Read(buffer)
  238. if err == io.EOF {
  239. err = nil
  240. }
  241. if err != nil {
  242. return n, ContextError(err)
  243. }
  244. if conn.readBuffer.Len() == 0 {
  245. conn.readState = nextState
  246. if conn.readState == OBFUSCATION_READ_STATE_FINISHED {
  247. // The buffer memory is no longer used
  248. conn.readBuffer = nil
  249. }
  250. }
  251. return n, nil
  252. }
  253. // transformAndWrite transforms the upstream bytes stream while in an
  254. // obfucation state, buffers bytes as necessary for parsing, and writes
  255. // transformed bytes to the network connection. Bytes are obfuscated until
  256. // after the first SSH_MSG_NEWKEYS packet is sent.
  257. //
  258. // There are two mode-specific states:
  259. //
  260. // State OBFUSCATION_WRITE_STATE_CLIENT_SEND_SEED_MESSAGE: the initial
  261. // state, when the client has not sent any data. In this state, the seed message
  262. // is injected into the client output stream.
  263. //
  264. // State OBFUSCATION_WRITE_STATE_SERVER_SEND_IDENTIFICATION_LINE_PADDING: the
  265. // initial state, when the server has not sent any data. In this state, the
  266. // additional lines of padding are injected into the server output stream.
  267. // This padding is a partial defense against traffic analysis against the
  268. // otherwise-fixed size server version line. This makes use of the
  269. // "other lines of data" allowance, before the version line, which clients
  270. // will ignore (http://tools.ietf.org/html/rfc4253#section-4.2).
  271. //
  272. // State OBFUSCATION_WRITE_STATE_IDENTIFICATION_LINE: before
  273. // packets are sent, the ssh peer sends an identification line terminated by CRLF:
  274. // http://www.ietf.org/rfc/rfc4253.txt sec 4.2.
  275. // In this state, the CRLF terminator is used to parse message boundaries.
  276. //
  277. // State OBFUSCATION_WRITE_STATE_KEX_PACKETS: follows the binary
  278. // packet protocol, parsing each packet until the first SSH_MSG_NEWKEYS.
  279. // http://www.ietf.org/rfc/rfc4253.txt sec 6:
  280. // uint32 packet_length
  281. // byte padding_length
  282. // byte[n1] payload; n1 = packet_length - padding_length - 1
  283. // byte[n2] random padding; n2 = padding_length
  284. // byte[m] mac (Message Authentication Code - MAC); m = mac_length
  285. // m is 0 as no MAC ha yet been negotiated.
  286. // http://www.ietf.org/rfc/rfc4253.txt sec 7.3, 12:
  287. // The payload for SSH_MSG_NEWKEYS is one byte, the packet type, value 21.
  288. //
  289. // SSH packet padding values are transformed to achieve random, variable length
  290. // padding during the KEX phase as a partial defense against traffic analysis.
  291. // (The transformer can do this since only the payload and not the padding of
  292. // these packets is authenticated in the "exchange hash").
  293. func (conn *ObfuscatedSshConn) transformAndWrite(buffer []byte) error {
  294. // The seed message (client) and identification line padding (server)
  295. // are injected before any standard SSH traffic.
  296. if conn.writeState == OBFUSCATION_WRITE_STATE_CLIENT_SEND_SEED_MESSAGE {
  297. _, err := conn.Conn.Write(conn.obfuscator.SendSeedMessage())
  298. if err != nil {
  299. return ContextError(err)
  300. }
  301. conn.writeState = OBFUSCATION_WRITE_STATE_IDENTIFICATION_LINE
  302. } else if conn.writeState == OBFUSCATION_WRITE_STATE_SERVER_SEND_IDENTIFICATION_LINE_PADDING {
  303. padding, err := makeServerIdentificationLinePadding()
  304. if err != nil {
  305. return ContextError(err)
  306. }
  307. conn.writeObfuscate(padding)
  308. _, err = conn.Conn.Write(padding)
  309. if err != nil {
  310. return ContextError(err)
  311. }
  312. conn.writeState = OBFUSCATION_WRITE_STATE_IDENTIFICATION_LINE
  313. }
  314. // writeBuffer is used to buffer bytes received from Write() until a
  315. // complete SSH message is received. transformBuffer is used as a scratch
  316. // buffer for size-changing tranformations, including padding transforms.
  317. // All data flows as follows:
  318. // conn.Write() -> writeBuffer -> transformBuffer -> conn.Conn.Write()
  319. conn.writeBuffer.Write(buffer)
  320. switch conn.writeState {
  321. case OBFUSCATION_WRITE_STATE_IDENTIFICATION_LINE:
  322. hasIdentificationLine := extractSshIdentificationLine(
  323. conn.writeBuffer, conn.transformBuffer)
  324. if hasIdentificationLine {
  325. conn.writeState = OBFUSCATION_WRITE_STATE_KEX_PACKETS
  326. }
  327. case OBFUSCATION_WRITE_STATE_KEX_PACKETS:
  328. hasMsgNewKeys, err := extractSshPackets(
  329. conn.writeBuffer, conn.transformBuffer)
  330. if err != nil {
  331. return ContextError(err)
  332. }
  333. if hasMsgNewKeys {
  334. conn.writeState = OBFUSCATION_WRITE_STATE_FINISHED
  335. }
  336. case OBFUSCATION_WRITE_STATE_FINISHED:
  337. return ContextError(errors.New("invalid write state"))
  338. }
  339. if conn.transformBuffer.Len() > 0 {
  340. sendData := conn.transformBuffer.Next(conn.transformBuffer.Len())
  341. conn.writeObfuscate(sendData)
  342. _, err := conn.Conn.Write(sendData)
  343. if err != nil {
  344. return ContextError(err)
  345. }
  346. }
  347. if conn.writeState == OBFUSCATION_WRITE_STATE_FINISHED {
  348. if conn.writeBuffer.Len() > 0 {
  349. // After SSH_MSG_NEWKEYS, any remaining bytes are un-obfuscated
  350. _, err := conn.Conn.Write(conn.writeBuffer.Bytes())
  351. if err != nil {
  352. return ContextError(err)
  353. }
  354. }
  355. // The buffer memory is no longer used
  356. conn.writeBuffer = nil
  357. conn.transformBuffer = nil
  358. }
  359. return nil
  360. }
  361. func readSshIdentificationLine(
  362. conn net.Conn,
  363. deobfuscate func([]byte),
  364. readBuffer *bytes.Buffer) error {
  365. // TODO: less redundant string searching?
  366. var oneByte [1]byte
  367. var validLine = false
  368. readBuffer.Grow(SSH_MAX_SERVER_LINE_LENGTH)
  369. for i := 0; i < SSH_MAX_SERVER_LINE_LENGTH; i++ {
  370. _, err := io.ReadFull(conn, oneByte[:])
  371. if err != nil {
  372. return ContextError(err)
  373. }
  374. deobfuscate(oneByte[:])
  375. readBuffer.WriteByte(oneByte[0])
  376. if bytes.HasSuffix(readBuffer.Bytes(), []byte("\r\n")) {
  377. validLine = true
  378. break
  379. }
  380. }
  381. if !validLine {
  382. return ContextError(errors.New("invalid identification line"))
  383. }
  384. return nil
  385. }
  386. func readSshPacket(
  387. conn net.Conn,
  388. deobfuscate func([]byte),
  389. readBuffer *bytes.Buffer) (bool, error) {
  390. prefixOffset := readBuffer.Len()
  391. readBuffer.Grow(SSH_PACKET_PREFIX_LENGTH)
  392. n, err := readBuffer.ReadFrom(io.LimitReader(conn, SSH_PACKET_PREFIX_LENGTH))
  393. if err == nil && n != SSH_PACKET_PREFIX_LENGTH {
  394. err = errors.New("unxpected number of bytes read")
  395. }
  396. if err != nil {
  397. return false, ContextError(err)
  398. }
  399. prefix := readBuffer.Bytes()[prefixOffset : prefixOffset+SSH_PACKET_PREFIX_LENGTH]
  400. deobfuscate(prefix)
  401. _, _, payloadLength, messageLength, err := getSshPacketPrefix(prefix)
  402. if err != nil {
  403. return false, ContextError(err)
  404. }
  405. remainingReadLength := messageLength - SSH_PACKET_PREFIX_LENGTH
  406. readBuffer.Grow(remainingReadLength)
  407. n, err = readBuffer.ReadFrom(io.LimitReader(conn, int64(remainingReadLength)))
  408. if err == nil && n != int64(remainingReadLength) {
  409. err = errors.New("unxpected number of bytes read")
  410. }
  411. if err != nil {
  412. return false, ContextError(err)
  413. }
  414. remainingBytes := readBuffer.Bytes()[prefixOffset+SSH_PACKET_PREFIX_LENGTH:]
  415. deobfuscate(remainingBytes)
  416. isMsgNewKeys := false
  417. if payloadLength > 0 {
  418. packetType := int(readBuffer.Bytes()[prefixOffset+SSH_PACKET_PREFIX_LENGTH])
  419. if packetType == SSH_MSG_NEWKEYS {
  420. isMsgNewKeys = true
  421. }
  422. }
  423. return isMsgNewKeys, nil
  424. }
  425. // From the original patch to sshd.c:
  426. // https://bitbucket.org/psiphon/psiphon-circumvention-system/commits/f40865ce624b680be840dc2432283c8137bd896d
  427. func makeServerIdentificationLinePadding() ([]byte, error) {
  428. paddingLength, err := MakeSecureRandomInt(OBFUSCATE_MAX_PADDING - 2) // 2 = CRLF
  429. if err != nil {
  430. return nil, ContextError(err)
  431. }
  432. paddingLength += 2
  433. padding := make([]byte, paddingLength)
  434. // For backwards compatibility with some clients, send no more than 512 characters
  435. // per line (including CRLF). To keep the padding distribution between 0 and OBFUSCATE_MAX_PADDING
  436. // characters, we send lines that add up to padding_length characters including all CRLFs.
  437. minLineLength := 2
  438. maxLineLength := 512
  439. lineStartIndex := 0
  440. for paddingLength > 0 {
  441. lineLength := paddingLength
  442. if lineLength > maxLineLength {
  443. lineLength = maxLineLength
  444. }
  445. // Leave enough padding allowance to send a full CRLF on the last line
  446. if paddingLength-lineLength > 0 &&
  447. paddingLength-lineLength < minLineLength {
  448. lineLength -= minLineLength - (paddingLength - lineLength)
  449. }
  450. padding[lineStartIndex+lineLength-2] = '\r'
  451. padding[lineStartIndex+lineLength-1] = '\n'
  452. lineStartIndex += lineLength
  453. paddingLength -= lineLength
  454. }
  455. return padding, nil
  456. }
  457. func extractSshIdentificationLine(writeBuffer, transformBuffer *bytes.Buffer) bool {
  458. index := bytes.Index(writeBuffer.Bytes(), []byte("\r\n"))
  459. if index != -1 {
  460. lineLength := index + 2 // + 2 for \r\n
  461. transformBuffer.Write(writeBuffer.Next(lineLength))
  462. return true
  463. }
  464. return false
  465. }
  466. func extractSshPackets(writeBuffer, transformBuffer *bytes.Buffer) (bool, error) {
  467. hasMsgNewKeys := false
  468. for writeBuffer.Len() >= SSH_PACKET_PREFIX_LENGTH {
  469. packetLength, paddingLength, payloadLength, messageLength, err := getSshPacketPrefix(
  470. writeBuffer.Bytes()[:SSH_PACKET_PREFIX_LENGTH])
  471. if err != nil {
  472. return false, ContextError(err)
  473. }
  474. if writeBuffer.Len() < messageLength {
  475. // We don't have the complete packet yet
  476. break
  477. }
  478. packet := writeBuffer.Next(messageLength)
  479. if payloadLength > 0 {
  480. packetType := int(packet[SSH_PACKET_PREFIX_LENGTH])
  481. if packetType == SSH_MSG_NEWKEYS {
  482. hasMsgNewKeys = true
  483. }
  484. }
  485. transformedPacketOffset := transformBuffer.Len()
  486. transformBuffer.Write(packet)
  487. transformedPacket := transformBuffer.Bytes()[transformedPacketOffset:]
  488. // Padding transformation
  489. // See RFC 4253 sec. 6 for constraints
  490. possiblePaddings := (SSH_MAX_PADDING_LENGTH - paddingLength) / SSH_PADDING_MULTIPLE
  491. if possiblePaddings > 0 {
  492. // selectedPadding is integer in range [0, possiblePaddings)
  493. selectedPadding, err := MakeSecureRandomInt(possiblePaddings)
  494. if err != nil {
  495. return false, ContextError(err)
  496. }
  497. extraPaddingLength := selectedPadding * SSH_PADDING_MULTIPLE
  498. extraPadding, err := MakeSecureRandomBytes(extraPaddingLength)
  499. if err != nil {
  500. return false, ContextError(err)
  501. }
  502. setSshPacketPrefix(
  503. transformedPacket,
  504. packetLength+extraPaddingLength,
  505. paddingLength+extraPaddingLength)
  506. transformBuffer.Write(extraPadding)
  507. }
  508. }
  509. return hasMsgNewKeys, nil
  510. }
  511. func getSshPacketPrefix(buffer []byte) (int, int, int, int, error) {
  512. packetLength := int(binary.BigEndian.Uint32(buffer[0 : SSH_PACKET_PREFIX_LENGTH-1]))
  513. if packetLength < 0 || packetLength > SSH_MAX_PACKET_LENGTH {
  514. return 0, 0, 0, 0, ContextError(errors.New("invalid ssh packet length"))
  515. }
  516. paddingLength := int(buffer[SSH_PACKET_PREFIX_LENGTH-1])
  517. payloadLength := packetLength - paddingLength - 1
  518. messageLength := SSH_PACKET_PREFIX_LENGTH + packetLength - 1
  519. return packetLength, paddingLength, payloadLength, messageLength, nil
  520. }
  521. func setSshPacketPrefix(buffer []byte, packetLength, paddingLength int) {
  522. binary.BigEndian.PutUint32(buffer, uint32(packetLength))
  523. buffer[SSH_PACKET_PREFIX_LENGTH-1] = byte(paddingLength)
  524. }