
module Rex
  module Parser
    module NetSarang
      # @author Kali-Team
      class NetSarangCrypto
        attr_accessor :version
        attr_accessor :username
        attr_accessor :sid
        attr_accessor :master_password
        attr_accessor :key

        # This class implements encryption and decryption of NetSarang
        #
        # @param type [String] only Xshell or Xftp.
        # @param version [String] Specify version of session file. e.g.:5.3
        # @param username [String] Specify username. This parameter will be used if version > 5.2.
        # @param sid [String] Specify SID. This parameter will be used if version >= 5.1.
        # @option master_password [String] Specify user's master password.
        #
        # @return [Rex::Parser::NetSarang::NetSarangCrypto] The NetSarangCrypto object
        def initialize(type, version, username, sid, master_password = nil)
          self.version = version.to_f
          self.username = username
          self.sid = sid
          self.master_password = master_password
          md5 = OpenSSL::Digest.new('MD5')
          sha256 = OpenSSL::Digest.new('SHA256')
          if (self.version > 0) && (self.version < 5.1)
            self.key = (type == 'Xshell') ? md5.digest('!X@s#h$e%l^l&') : md5.digest('!X@s#c$e%l^l&')
          elsif (self.version >= 5.1) && (self.version <= 5.2)
            self.key = sha256.digest(self.sid)
          elsif (self.version > 5.2)
            if self.master_password.nil?
              self.key = sha256.digest(self.username + self.sid)
            else
              self.key = sha256.digest(self.master_password)
            end
          else
            raise 'Invalid argument: version'
          end
        end

        # Encrypt
        #
        # @param string [String]
        # @return [String] ciphertext
        def encrypt_string(string)
          cipher = Rex::Crypto.rc4(key, string)
          if (version < 5.1)
            return Rex::Text.encode_base64(cipher)
          else
            sha256 = OpenSSL::Digest.new('SHA256')
            checksum = sha256.digest(string)
            ciphertext = cipher
            return Rex::Text.encode_base64(ciphertext + checksum)
          end
        end

        # Decrypt
        #
        # @param string [String]
        # @return [String] plaintext failed return nil
        def decrypt_string(string)
          if (version < 5.1)
            return Rex::Crypto.rc4(key, Rex::Text.decode_base64(string))
          else
            data = Rex::Text.decode_base64(string)
            ciphertext = data[0, data.length - 0x20]
            plaintext = Rex::Crypto.rc4(key, ciphertext)
            if plaintext.is_utf8?
              return plaintext
            else
              return nil
            end
          end
        end
      end

      # Parse xsh session file provided as a string.
      #
      # @param input [String] XSH Session file as a string
      # @return [Array] An array containing the version, host,
      #   port, username, and password obtained from the XSH session file.
      def parser_xsh(input)
        ini = Rex::Parser::Ini.from_s(input)
        version = ini['SessionInfo']['Version']
        port = ini['CONNECTION']['Port']
        host = ini['CONNECTION']['Host']
        username = ini['CONNECTION:AUTHENTICATION']['UserName']
        password = ini['CONNECTION:AUTHENTICATION']['Password'] || nil
        [version, host, port, username, password]
      end

      # parser xfp session file
      #
      # @param ini [String]
      # @return [version, host, port, username, password]
      def parser_xfp(file)
        ini = Rex::Parser::Ini.from_s(file)
        version = ini['SessionInfo']['Version']
        port = ini['Connection']['Port']
        host = ini['Connection']['Host']
        username = ini['Connection']['UserName']
        password = ini['Connection']['Password']
        [version, host, port, username, password]
      end
    end
  end
end
