Explorar el Código

added random, variable length padding (as is done in other Psiphon clients)

Rod Hynes hace 11 años
padre
commit
059b6eb145
Se han modificado 1 ficheros con 35 adiciones y 7 borrados
  1. 35 7
      psiphon/obfuscatedSshConn.go

+ 35 - 7
psiphon/obfuscatedSshConn.go

@@ -65,10 +65,12 @@ type ObfuscatedSshConn struct {
 }
 
 const (
-	MAX_SERVER_LINE_LENGTH   = 1024
-	SSH_PACKET_PREFIX_LENGTH = 5          // uint32 + byte
-	SSH_MAX_PACKET_LENGTH    = 256 * 1024 // OpenSSH max packet length
-	SSH_MSG_NEWKEYS          = 21
+	SSH_MAX_SERVER_LINE_LENGTH = 1024
+	SSH_PACKET_PREFIX_LENGTH   = 5          // uint32 + byte
+	SSH_MAX_PACKET_LENGTH      = 256 * 1024 // OpenSSH max packet length
+	SSH_MSG_NEWKEYS            = 21
+	SSH_MAX_PADDING_LENGTH     = 255 // RFC 4253 sec. 6
+	SSH_PADDING_MULTIPLE       = 16  // Default cipher block size
 )
 
 // NewObfuscatedSshConn creates a new ObfuscatedSshConn. The underlying
@@ -157,7 +159,7 @@ func (conn *ObfuscatedSshConn) readAndTransform(buffer []byte) (n int, err error
 				// TODO: use bufio.BufferedReader? less redundant string searching?
 				var oneByte [1]byte
 				var validLine = false
-				for len(conn.readBuffer) < MAX_SERVER_LINE_LENGTH {
+				for len(conn.readBuffer) < SSH_MAX_SERVER_LINE_LENGTH {
 					_, err := io.ReadFull(conn.Conn, oneByte[:])
 					if err != nil {
 						return 0, err
@@ -245,6 +247,11 @@ func (conn *ObfuscatedSshConn) readAndTransform(buffer []byte) (n int, err error
 // m is 0 as no MAC ha yet been negotiated.
 // http://www.ietf.org/rfc/rfc4253.txt sec 7.3, 12:
 // The payload for SSH_MSG_NEWKEYS is one byte, the packet type, value 21.
+//
+// SSH packet padding values are transformed to achive random, variable length
+// padding during the KEX phase as a partial defense against traffic analysis.
+// (The transformer can do this since only the payload and not the padding of
+// these packets is authenticated in the "exchange hash").
 func (conn *ObfuscatedSshConn) transformAndWrite(buffer []byte) (err error) {
 	if conn.writeState == OBFUSCATION_WRITE_STATE_SEND_CLIENT_SEED_MESSAGE {
 		_, err = conn.Conn.Write(conn.obfuscator.ConsumeSeedMessage())
@@ -266,12 +273,11 @@ func (conn *ObfuscatedSshConn) transformAndWrite(buffer []byte) (err error) {
 		}
 	case OBFUSCATION_WRITE_STATE_CLIENT_KEX_PACKETS:
 		for len(conn.writeBuffer) >= SSH_PACKET_PREFIX_LENGTH {
-			_, _, payloadLength, messageLength := getSshPacketPrefix(conn.writeBuffer)
+			packetLength, paddingLength, payloadLength, messageLength := getSshPacketPrefix(conn.writeBuffer)
 			if len(conn.writeBuffer) < messageLength {
 				// We don't have the complete packet yet
 				break
 			}
-			// TODO: transform padding to implement random, variable length padding in KEX phase
 			messageBuffer = append([]byte(nil), conn.writeBuffer[:messageLength]...)
 			conn.writeBuffer = conn.writeBuffer[messageLength:]
 			if payloadLength > 0 {
@@ -280,6 +286,23 @@ func (conn *ObfuscatedSshConn) transformAndWrite(buffer []byte) (err error) {
 					conn.writeState = OBFUSCATION_WRITE_STATE_FINISHED
 				}
 			}
+			// Padding transformation
+			// See RFC 4253 sec. 6 for constraints
+			possiblePaddings := (SSH_MAX_PADDING_LENGTH - paddingLength) / SSH_PADDING_MULTIPLE
+			if possiblePaddings > 0 {
+				selectedPadding, err := MakeSecureRandomInt(possiblePaddings)
+				if err != nil {
+					return err
+				}
+				extraPaddingLength := selectedPadding * SSH_PADDING_MULTIPLE
+				extraPadding, err := MakeSecureRandomBytes(extraPaddingLength)
+				if err != nil {
+					return err
+				}
+				setSshPacketPrefix(
+					messageBuffer, packetLength+extraPaddingLength, paddingLength+extraPaddingLength)
+				messageBuffer = append(messageBuffer, extraPadding...)
+			}
 		}
 	case OBFUSCATION_WRITE_STATE_FINISHED:
 		panic("ObfuscatedSshConn: invalid write state")
@@ -311,3 +334,8 @@ func getSshPacketPrefix(buffer []byte) (packetLength, paddingLength, payloadLeng
 	messageLength = SSH_PACKET_PREFIX_LENGTH + packetLength - 1
 	return
 }
+
+func setSshPacketPrefix(buffer []byte, packetLength, paddingLength int) {
+	binary.BigEndian.PutUint32(buffer, uint32(packetLength))
+	buffer[SSH_PACKET_PREFIX_LENGTH-1] = byte(paddingLength)
+}