Class: LastPass::Fetcher
- Inherits:
-
Object
- Object
- LastPass::Fetcher
- Defined in:
- lib/lastpass/fetcher.rb
Class Method Summary collapse
- .create_session(parsed_response, key_iteration_count) ⇒ Object
- .decode_blob(blob) ⇒ Object
- .fetch(session, web_client = HTTParty) ⇒ Object
- .login(username, password, multifactor_password = nil) ⇒ Object
- .login_error(parsed_response) ⇒ Object
- .make_hash(username, password, key_iteration_count) ⇒ Object
- .make_key(username, password, key_iteration_count) ⇒ Object
- .request_iteration_count(username, web_client = HTTParty) ⇒ Object
- .request_login(username, password, key_iteration_count, multifactor_password = nil, web_client = HTTParty) ⇒ Object
Class Method Details
.create_session(parsed_response, key_iteration_count) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/lastpass/fetcher.rb', line 68 def self.create_session parsed_response, key_iteration_count ok = parsed_response["ok"] if ok.is_a? Hash session_id = ok["sessionid"] if session_id.is_a? String return Session.new session_id, key_iteration_count end end nil end |
.decode_blob(blob) ⇒ Object
102 103 104 105 |
# File 'lib/lastpass/fetcher.rb', line 102 def self.decode_blob blob # TODO: Check for invalid base64 Base64.decode64 blob end |
.fetch(session, web_client = HTTParty) ⇒ Object
11 12 13 14 15 16 17 18 19 |
# File 'lib/lastpass/fetcher.rb', line 11 def self.fetch session, web_client = HTTParty response = web_client.get "https://lastpass.com/getaccts.php?mobile=1&b64=1&hash=0.0&hasplugin=3.0.23&requestsrc=android", format: :plain, cookies: {"PHPSESSID" => URI.encode(session.id)} raise NetworkError unless response.response.is_a? Net::HTTPOK Blob.new decode_blob(response.parsed_response), session.key_iteration_count end |
.login(username, password, multifactor_password = nil) ⇒ Object
6 7 8 9 |
# File 'lib/lastpass/fetcher.rb', line 6 def self.login username, password, multifactor_password = nil key_iteration_count = request_iteration_count username request_login username, password, key_iteration_count, multifactor_password end |
.login_error(parsed_response) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/lastpass/fetcher.rb', line 80 def self.login_error parsed_response error = (parsed_response["response"] || {})["error"] return UnknownResponseSchemaError unless error.is_a? Hash exceptions = { "unknownemail" => LastPassUnknownUsernameError, "unknownpassword" => LastPassInvalidPasswordError, "googleauthrequired" => LastPassIncorrectGoogleAuthenticatorCodeError, "googleauthfailed" => LastPassIncorrectGoogleAuthenticatorCodeError, "yubikeyrestricted" => LastPassIncorrectYubikeyPasswordError, } cause = error["cause"] = error["message"] if cause (exceptions[cause] || LastPassUnknownError).new || cause else InvalidResponseError.new end end |
.make_hash(username, password, key_iteration_count) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/lastpass/fetcher.rb', line 121 def self.make_hash username, password, key_iteration_count if key_iteration_count == 1 Digest::SHA256.hexdigest Digest.hexencode(make_key(username, password, 1)) + password else PBKDF2 .new(password: make_key(username, password, key_iteration_count), salt: password, iterations: 1, key_length: 32) .hex_string end end |
.make_key(username, password, key_iteration_count) ⇒ Object
107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/lastpass/fetcher.rb', line 107 def self.make_key username, password, key_iteration_count if key_iteration_count == 1 Digest::SHA256.digest username + password else PBKDF2 .new(password: password, salt: username, iterations: key_iteration_count, key_length: 32) .bin_string .force_encoding "BINARY" end end |
.request_iteration_count(username, web_client = HTTParty) ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/lastpass/fetcher.rb', line 21 def self.request_iteration_count username, web_client = HTTParty response = web_client.post "https://lastpass.com/iterations.php", query: {email: username} raise NetworkError unless response.response.is_a? Net::HTTPOK begin count = Integer response.parsed_response rescue ArgumentError raise InvalidResponseError, "Key iteration count is invalid" end raise InvalidResponseError, "Key iteration count is not positive" unless count > 0 count end |
.request_login(username, password, key_iteration_count, multifactor_password = nil, web_client = HTTParty) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/lastpass/fetcher.rb', line 38 def self.request_login username, password, key_iteration_count, multifactor_password = nil, web_client = HTTParty body = { method: "mobile", web: 1, xml: 1, username: username, hash: make_hash(username, password, key_iteration_count), iterations: key_iteration_count } body[:otp] = multifactor_password if multifactor_password response = web_client.post "https://lastpass.com/login.php", format: :xml, body: body raise NetworkError unless response.response.is_a? Net::HTTPOK parsed_response = response.parsed_response raise InvalidResponseError unless parsed_response.is_a? Hash create_session parsed_response, key_iteration_count or raise login_error parsed_response end |