Module: Lich::Common::Authentication::EAccess
- Defined in:
- documented/common/authentication/eaccess.rb
Defined Under Namespace
Classes: AuthenticationError
Constant Summary collapse
- PACKET_SIZE =
8192
Class Method Summary collapse
-
.auth(password:, account:, character: nil, game_code: nil, legacy: false) ⇒ Array<Hash>
Authenticates a user with the provided credentials.
-
.download_pem(hostname = "eaccess.play.net", port = 7910) ⇒ void
Downloads the PEM file from the specified hostname and port.
-
.pem ⇒ String
Returns the path to the PEM file used for SSL connections.
-
.pem_exist? ⇒ Boolean
Checks if the PEM file exists on the filesystem.
-
.read(conn) ⇒ String
Reads data from the given connection up to the defined packet size.
-
.socket(hostname = "eaccess.play.net", port = 7910) ⇒ OpenSSL::SSL::SSLSocket
Establishes a secure socket connection to the specified hostname and port.
-
.verify_pem(conn) ⇒ Boolean
Verifies the PEM certificate against the stored PEM file.
Class Method Details
.auth(password:, account:, character: nil, game_code: nil, legacy: false) ⇒ Array<Hash>
Authenticates a user with the provided credentials.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'documented/common/authentication/eaccess.rb', line 94 def self.auth(password:, account:, character: nil, game_code: nil, legacy: false) # Set Account module state if defined?(Lich::Common::Account) Lich::Common::Account.name = account Lich::Common::Account.game_code = game_code Lich::Common::Account.character = character end conn = EAccess.socket() begin # it is vitally important to verify self-signed certs # because there is no chain-of-trust for them EAccess.verify_pem(conn) conn.puts "K\n" hashkey = EAccess.read(conn) # pp "hash=%s" % hashkey password = password.split('').map { |c| c.getbyte(0) } hashkey = hashkey.split('').map { |c| c.getbyte(0) } password.each_index { |i| password[i] = ((password[i] - 32) ^ hashkey[i]) + 32 } password = password.map { |c| c.chr }.join conn.puts "A\t#{account}\t#{password}\n" response = EAccess.read(conn) unless /KEY\t(?<key>.*)\t/.match(response) error_code = response.split(/\s+/).last raise AuthenticationError, error_code end # pp "A:response=%s" % response conn.puts "M\n" response = EAccess.read(conn) raise StandardError, response unless response =~ /^M\t/ # pp "M:response=%s" % response unless legacy conn.puts "F\t#{game_code}\n" response = EAccess.read(conn) raise StandardError, response unless response =~ /NORMAL|PREMIUM|TRIAL|INTERNAL|FREE/ if defined?(Lich::Common::Account) Lich::Common::Account.subscription = response end # pp "F:response=%s" % response conn.puts "G\t#{game_code}\n" EAccess.read(conn) # pp "G:response=%s" % response conn.puts "P\t#{game_code}\n" EAccess.read(conn) # pp "P:response=%s" % response conn.puts "C\n" response = EAccess.read(conn) # pp "C:response=%s" % response if defined?(Lich::Common::Account) Lich::Common::Account.members = response end char_entry = response.sub(/^C\t[0-9]+\t[0-9]+\t[0-9]+\t[0-9]+[\t\n]/, '') .scan(/[^\t]+\t[^\t^\n]+/) .find { |c| c.split("\t")[1] == character } unless char_entry raise AuthenticationError, "CHARACTER_NOT_FOUND" end char_code = char_entry.split("\t")[0] conn.puts "L\t#{char_code}\tSTORM\n" response = EAccess.read(conn) raise StandardError, response unless response =~ /^L\t/ # pp "L:response=%s" % response login_info = response.sub(/^L\tOK\t/, '') .split("\t") .map { |kv| k, v = kv.split("=") [k.downcase, v] }.to_h else login_info = Array.new for game in response.sub(/^M\t/, '').scan(/[^\t]+\t[^\t^\n]+/) game_code, game_name = game.split("\t") # pp "M:response = %s" % response conn.puts "N\t#{game_code}\n" response = EAccess.read(conn) if response =~ /STORM/ conn.puts "F\t#{game_code}\n" response = EAccess.read(conn) if response =~ /NORMAL|PREMIUM|TRIAL|INTERNAL|FREE/ if defined?(Lich::Common::Account) Lich::Common::Account.subscription = response end conn.puts "G\t#{game_code}\n" EAccess.read(conn) conn.puts "P\t#{game_code}\n" EAccess.read(conn) conn.puts "C\n" response = EAccess.read(conn) if defined?(Lich::Common::Account) Lich::Common::Account.members = response end for code_name in response.sub(/^C\t[0-9]+\t[0-9]+\t[0-9]+\t[0-9]+[\t\n]/, '').scan(/[^\t]+\t[^\t^\n]+/) char_code, char_name = code_name.split("\t") hash = { :game_code => "#{game_code}", :game_name => "#{game_name}", :char_code => "#{char_code}", :char_name => "#{char_name}" } login_info.push(hash) end end end end end return login_info ensure conn&.close unless conn&.closed? end end |
.download_pem(hostname = "eaccess.play.net", port = 7910) ⇒ void
This method returns an undefined value.
Downloads the PEM file from the specified hostname and port.
40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'documented/common/authentication/eaccess.rb', line 40 def self.download_pem(hostname = "eaccess.play.net", port = 7910) # Create an OpenSSL context ctx = OpenSSL::SSL::SSLContext.new # Get remote TCP socket sock = TCPSocket.new(hostname, port) # pass that socket to OpenSSL ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) # establish connection, if possible ssl.connect # write the .pem to disk File.write(pem, ssl.peer_cert) end |
.pem ⇒ String
Returns the path to the PEM file used for SSL connections.
26 27 28 |
# File 'documented/common/authentication/eaccess.rb', line 26 def self.pem @pem ||= File.join(DATA_DIR, "simu.pem") end |
.pem_exist? ⇒ Boolean
Checks if the PEM file exists on the filesystem.
32 33 34 |
# File 'documented/common/authentication/eaccess.rb', line 32 def self.pem_exist? File.exist? pem end |
.read(conn) ⇒ String
Reads data from the given connection up to the defined packet size.
205 206 207 |
# File 'documented/common/authentication/eaccess.rb', line 205 def self.read(conn) conn.sysread(PACKET_SIZE) end |
.socket(hostname = "eaccess.play.net", port = 7910) ⇒ OpenSSL::SSL::SSLSocket
Establishes a secure socket connection to the specified hostname and port.
72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'documented/common/authentication/eaccess.rb', line 72 def self.socket(hostname = "eaccess.play.net", port = 7910) download_pem unless pem_exist? socket = TCPSocket.open(hostname, port) cert_store = OpenSSL::X509::Store.new ssl_context = OpenSSL::SSL::SSLContext.new ssl_context.cert_store = cert_store ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER cert_store.add_file(pem) if pem_exist? ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context) ssl_socket.sync_close = true EAccess.verify_pem(ssl_socket.connect) return ssl_socket end |
.verify_pem(conn) ⇒ Boolean
Verifies the PEM certificate against the stored PEM file.
57 58 59 60 61 62 63 64 65 66 |
# File 'documented/common/authentication/eaccess.rb', line 57 def self.verify_pem(conn) # return if conn.peer_cert.to_s = File.read(pem) if !(conn.peer_cert.to_s == File.read(pem)) Lich.log "Exception, \nssl peer certificate did not match #{pem}\nwas:\n#{conn.peer_cert}" download_pem else return true end # fail Exception, "\nssl peer certificate did not match #{pem}\nwas:\n#{conn.peer_cert}" end |