diff --git a/.travis.yml b/.travis.yml index 74b23ed..f59f0de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: python python: - "2.7" + - "3.3" install: - "pip install -r requirements.txt" script: diff --git a/README.rst b/README.rst index ce8d939..cdfa205 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ Example vault = lastpass.Vault.open_remote(username, password) for i in vault.accounts: - print i.id, i.username, i.password, i.url + print(i.id, i.username, i.password, i.url) License diff --git a/example/example.py b/example/example.py index c5243b9..38cfd5a 100644 --- a/example/example.py +++ b/example/example.py @@ -26,4 +26,4 @@ for index, i in enumerate(vault.accounts): - print index+1, i.id, i.name, i.username, i.password, i.url, i.group + print("{} {} {} {} {} {} {}".format(index + 1, i.id, i.name, i.username, i.password, i.url, i.group)) diff --git a/lastpass/blob.py b/lastpass/blob.py index 66c3c8a..3acd920 100644 --- a/lastpass/blob.py +++ b/lastpass/blob.py @@ -1,13 +1,12 @@ # coding: utf-8 -import fetcher - class Blob(object): def __init__(self, bytes, key_iteration_count): self.bytes = bytes self.key_iteration_count = key_iteration_count def encryption_key(self, username, password): - return fetcher.Fetcher.make_key(username, password, self.key_iteration_count) + from .fetcher import Fetcher + return Fetcher.make_key(username, password, self.key_iteration_count) def __eq__(self, other): return self.bytes == other.bytes and self.key_iteration_count == other.key_iteration_count diff --git a/lastpass/fetcher.py b/lastpass/fetcher.py index b91385c..435e3bc 100644 --- a/lastpass/fetcher.py +++ b/lastpass/fetcher.py @@ -1,12 +1,14 @@ # coding: utf-8 -import httplib -import pbkdf2 import hashlib +from base64 import b64decode +from binascii import hexlify +from Crypto.Hash import HMAC, SHA256 +from Crypto.Protocol.KDF import PBKDF2 import requests #from lxml import etree from xml.etree import ElementTree as etree -import blob -from exceptions import ( +from . import blob +from .exceptions import ( NetworkError, InvalidResponseError, UnknownResponseSchemaError, @@ -16,7 +18,7 @@ LastPassIncorrectYubikeyPasswordError, LastPassUnknownError ) -from session import Session +from .session import Session class Fetcher(object): @@ -30,7 +32,7 @@ def fetch(cls, session, web_client=requests): response = web_client.get('https://lastpass.com/getaccts.php?mobile=1&b64=1&hash=0.0', cookies={'PHPSESSID': session.id}) - if response.status_code != httplib.OK: + if response.status_code != requests.codes.ok: raise NetworkError() return blob.Blob(cls.decode_blob(response.content), session.key_iteration_count) @@ -39,7 +41,7 @@ def fetch(cls, session, web_client=requests): def request_iteration_count(cls, username, web_client=requests): response = web_client.post('https://lastpass.com/iterations.php', data={'email': username}) - if response.status_code != httplib.OK: + if response.status_code != requests.codes.ok: raise NetworkError() try: @@ -68,7 +70,7 @@ def request_login(cls, username, password, key_iteration_count, multifactor_pass response = web_client.post('https://lastpass.com/login.php', data=body) - if response.status_code != httplib.OK: + if response.status_code != requests.codes.ok: raise NetworkError() try: @@ -88,7 +90,7 @@ def request_login(cls, username, password, key_iteration_count, multifactor_pass def create_session(cls, parsed_response, key_iteration_count): if parsed_response.tag == 'ok': session_id = parsed_response.attrib.get('sessionid') - if isinstance(session_id, basestring): + if isinstance(session_id, str): return Session(session_id, key_iteration_count) @classmethod @@ -114,24 +116,26 @@ def login_error(cls, parsed_response): @classmethod def decode_blob(cls, blob): - return blob.decode('base64') + return b64decode(blob) @classmethod def make_key(cls, username, password, key_iteration_count): if key_iteration_count == 1: - return hashlib.sha256(username + password).digest() + return hashlib.sha256(username.encode() + password.encode()).digest() else: - return pbkdf2.pbkdf2_bin(password, username, key_iteration_count, 32, hashlib.sha256) + prf = lambda p, s: HMAC.new(p, s, SHA256).digest() + return PBKDF2(password.encode(), username.encode(), 32, key_iteration_count, prf) @classmethod def make_hash(cls, username, password, key_iteration_count): if key_iteration_count == 1: - return hashlib.sha256(cls.make_key(username, password, 1).encode('hex') + password).hexdigest() + return bytearray(hashlib.sha256(hexlify(cls.make_key(username, password, 1)) + password.encode()).hexdigest(), 'ascii') else: - return pbkdf2.pbkdf2_hex( + prf = lambda p, s: HMAC.new(p, s, SHA256).digest() + return hexlify(PBKDF2( cls.make_key(username, password, key_iteration_count), - password, - 1, + password.encode(), 32, - hashlib.sha256) + 1, + prf)) diff --git a/lastpass/parser.py b/lastpass/parser.py index 2b93b2e..cfebf09 100644 --- a/lastpass/parser.py +++ b/lastpass/parser.py @@ -1,11 +1,14 @@ # coding: utf-8 -from StringIO import StringIO +from base64 import b64decode +import binascii +import codecs +from io import BytesIO from collections import OrderedDict -from chunk import Chunk import struct from Crypto.Cipher import AES -from account import Account +from .account import Account +from .chunk import Chunk class Parser(object): @@ -13,8 +16,12 @@ class Parser(object): @classmethod def extract_chunks(cls, blob): chunks = OrderedDict() - stream = StringIO(blob.bytes) - while stream.pos < stream.len: + stream = BytesIO(blob.bytes) + current_pos = stream.tell() + stream.seek(0, 2) + length = stream.tell() + stream.seek(current_pos, 0) + while stream.tell() < length: chunk = cls.read_chunk(stream) if not chunks.get(chunk.id): chunks[chunk.id] = [] @@ -26,7 +33,7 @@ def extract_chunks(cls, blob): # TODO: See if this should be part of Account class. @classmethod def parse_account(cls, chunk, encryption_key): - io = StringIO(chunk.payload) + io = BytesIO(chunk.payload) id = cls.read_item(io) name = cls.decode_aes256_auto(cls.read_item(io), encryption_key) group = cls.decode_aes256_auto(cls.read_item(io), encryption_key) @@ -91,12 +98,15 @@ def read_uint32(cls, stream): # Decodes a hex encoded string into raw bytes. @classmethod def decode_hex(cls, data): - return data.decode('hex') + try: + return codecs.decode(data, 'hex_codec') + except binascii.Error: + raise TypeError() # Decodes a base64 encoded string into raw bytes. @classmethod def decode_base64(cls, data): - return data.decode('base64') + return b64decode(data) # Guesses AES encoding/cipher from the length of the data. # Possible combinations are: @@ -109,7 +119,7 @@ def decode_aes256_auto(cls, data, encryption_key): length64 = length % 64 if length == 0: - return '' + return b'' elif length16 == 0: return cls.decode_aes256_ecb_plain(data, encryption_key) elif length64 == 0 or length64 == 24 or length64 == 44: @@ -125,7 +135,7 @@ def decode_aes256_auto(cls, data, encryption_key): @classmethod def decode_aes256_ecb_plain(cls, data, encryption_key): if not data: - return '' + return b'' else: return cls.decode_aes256('ecb', '', data, encryption_key) @@ -138,7 +148,7 @@ def decode_aes256_ecb_base64(cls, data, encryption_key): @classmethod def decode_aes256_cbc_plain(cls, data, encryption_key): if not data: - return '' + return b'' else: # LastPass AES-256/CBC encryted string starts with an "!". # Next 16 bytes are the IV for the cipher. @@ -149,7 +159,7 @@ def decode_aes256_cbc_plain(cls, data, encryption_key): @classmethod def decode_aes256_cbc_base64(cls, data, encryption_key): if not data: - return '' + return b'' else: # LastPass AES-256/CBC/base64 encryted string starts with an "!". # Next 24 bytes are the base64 encoded IV for the cipher. @@ -175,5 +185,5 @@ def decode_aes256(cls, cipher, iv, data, encryption_key): aes = AES.new(encryption_key, aes_mode, iv) d = aes.decrypt(data) # http://passingcuriosity.com/2009/aes-encryption-in-python-with-m2crypto/ - unpad = lambda s: s[0:-ord(s[-1])] + unpad = lambda s: s[0:-ord(d[-1:])] return unpad(d) diff --git a/lastpass/vault.py b/lastpass/vault.py index 0b8d503..d06bcd8 100644 --- a/lastpass/vault.py +++ b/lastpass/vault.py @@ -1,5 +1,6 @@ # coding: utf-8 -import parser, fetcher +from .fetcher import Fetcher +from .parser import Parser class Vault(object): @@ -22,9 +23,9 @@ def open(cls, blob, username, password): # Just fetches the blob, could be used to store it locally @classmethod def fetch_blob(cls, username, password, multifactor_password=None): - return fetcher.Fetcher.fetch(fetcher.Fetcher.login(username, password, multifactor_password)) + return Fetcher.fetch(Fetcher.login(username, password, multifactor_password)) # This more of an internal method, use one of the static constructors instead def __init__(self, blob, encryption_key): - chunks = parser.Parser.extract_chunks(blob) - self.accounts = [parser.Parser.parse_account(i, encryption_key) for i in chunks['ACCT']] + chunks = Parser.extract_chunks(blob) + self.accounts = [Parser.parse_account(i, encryption_key) for i in chunks[b'ACCT']] diff --git a/requirements.txt b/requirements.txt index a043c47..250e6b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -requests==1.1.0 -simple-pbkdf2==1.0 +requests>=1.2.1,<=3.0.0 pycrypto==2.6.1 mock==1.0.1 \ No newline at end of file diff --git a/tests/test_blob.py b/tests/test_blob.py index 1532bb9..d38cf6c 100644 --- a/tests/test_blob.py +++ b/tests/test_blob.py @@ -1,15 +1,16 @@ # coding: utf-8 +from base64 import b64decode import unittest from lastpass.blob import Blob class BlobTestCase(unittest.TestCase): def setUp(self): - self.bytes = 'TFBBVgAAAAMxMjJQUkVNAAAACjE0MTQ5'.decode('base64') + self.bytes = b64decode('TFBBVgAAAAMxMjJQUkVNAAAACjE0MTQ5') self.key_iteration_count = 500 self.username = 'postlass@gmail.com' self.password = 'pl1234567890' - self.encryption_key = 'OfOUvVnQzB4v49sNh4+PdwIFb9Fr5+jVfWRTf+E2Ghg='.decode('base64') + self.encryption_key = b64decode('OfOUvVnQzB4v49sNh4+PdwIFb9Fr5+jVfWRTf+E2Ghg=') self.blob = Blob(self.bytes, self.key_iteration_count) diff --git a/tests/test_data.py b/tests/test_data.py index 2a19151..a12e3f7 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,12 +1,13 @@ # coding: utf-8 +from base64 import b64decode from lastpass.account import Account TEST_CHUNK_IDS = [ - 'LPAV', 'ATVR', 'ENCU', 'CBCU', 'BBTE', 'IPTE', 'WMTE', 'ANTE', 'DOTE', 'FETE', 'FUTE', - 'SYTE', 'WOTE', 'TATE', 'WPTE', 'SPMT', 'NMAC', 'ACCT', 'EQDN', 'URUL', 'ENDM', + b'LPAV', b'ATVR', b'ENCU', b'CBCU', b'BBTE', b'IPTE', b'WMTE', b'ANTE', b'DOTE', b'FETE', b'FUTE', + b'SYTE', b'WOTE', b'TATE', b'WPTE', b'SPMT', b'NMAC', b'ACCT', b'EQDN', b'URUL', b'ENDM', ] -TEST_BLOB = ("TFBBVgAAAAMxMThBVFZSAAAAAzEwMkVOQ1UAAABGITExVkVjWFVnelFramxranB2" + +TEST_BLOB = b64decode("TFBBVgAAAAMxMThBVFZSAAAAAzEwMkVOQ1UAAABGITExVkVjWFVnelFramxranB2" + "VGZyR3c9PXxYUklhVVdQYlQzRXdRQlZCRUYxZUJTTDI3bWZ1cVViZmFCV3JCYnd5" + "WFdFPUNCQ1UAAAABMUJCVEUAAAAMLTYyMTY5OTY2MDAwSVBURQAAAAwtNjIxNjk5" + "NjYwMDBXTVRFAAAADC02MjE2OTk2NjAwMEFOVEUAAAAMLTYyMTY5OTY2MDAwRE9U" + @@ -887,110 +888,110 @@ "MFVSVUwAAAAkAAAAFjc3NjU2MjZlNmY2NDY1MmU2MzZmNmQAAAABMQAAAAEwVVJV" + "TAAAACwAAAAeNmY2ZTZkNjk2MzcyNmY3MzZmNjY3NDJlNjM2ZjZkAAAAATEAAAAB" + "MFVSVUwAAAAoAAAAGjY4NjU3MjZmNmI3NTYxNzA3MDJlNjM2ZjZkAAAAATEAAAAB" + - "MFVSVUwAAAAaAAAADDZlNmY3NjJlNzI3NQAAAAExAAAAATBFTkRNAAAAAk9L").decode('base64') + "MFVSVUwAAAAaAAAADDZlNmY3NjJlNzI3NQAAAAExAAAAATBFTkRNAAAAAk9L") -TEST_ENCRYPTION_KEY = "p8utF7ZB8yD06SrtrD4hsdvEOiBU1Y19cr2dhG9DWZg=".decode('base64') +TEST_ENCRYPTION_KEY = b64decode("p8utF7ZB8yD06SrtrD4hsdvEOiBU1Y19cr2dhG9DWZg=") TEST_KEY_ITERATION_COUNT = 5000 TEST_ACCOUNTS = [ - Account("1872745596", "Muller, Morar and Wisoky", "branson_cormier", "8jgLCzQSkB2rTZ1OtF9sNGpc", "http://nienow.net/meagan.greenholt", "three"), - Account("1872745606", "goyette.net", "kris_quigley@baileyjewe.biz", "S5@3^wPv!6JsFj", "http://bechtelar.biz/tristian.barrows", "four"), - Account("1872745616", "Ward Inc", "angela_emard", "zp8N@KoWyS0IYu7VR$dvBF!t", "http://schuster.name/ashton", "one"), - Account("1872745626", "stehr.com", "bailee_marvin@mohrlegros.net", "cupiditate", "http://runolfon.org/te", "three"), - Account("1872745636", "kiehn.biz", "freda", "et", "http://hintzprohaska.biz/wade.fisher", "one"), - Account("1872745646", "Jacobs and Sons", "johnnie.hand", "gzyl6714", "http://schultzheaney.org/arvid", ""), - Account("1872745656", "Larkin, Kautzer and Wiegand", "hilton", "zguovmdr8703", "http://streich.com/ramona", "one"), - Account("1872745666", "Conn Inc", "malvina_paucek@nikolausveum.net", "numquam", "http://conn.net/leda", "four"), - Account("1872745676", "Block, Sanford and Connelly", "marilie_wolff", "zKcy?U*aCGS^gf@Z", "http://conroy.biz/zachery", "two"), - Account("1872745686", "gradyrenner.org", "oswald@ryan.info", "ojgwad28", "http://kihn.org/candice", ""), - Account("1872745696", "lesch.net", "nicholas", "Pkc72Lmr1qwI%sNV^d4@GtX", "http://glover.name/jerad", "two"), - Account("1872745706", "sipes.biz", "kaitlyn.bernier@reichel.net", "in", "http://mayert.name/jeromy", "two"), - Account("1872745716", "Hintz-Herman", "prince.moriette", "0hebvIS@s^BwMc", "http://sanfordwunsch.org/alek", ""), - Account("1872745726", "Hammes-Kassulke", "brooke@gloverhintz.net", "paokcs08", "http://lehner.biz/stanley.dooley", "four"), - Account("1872745736", "hermann.com", "jasper_dickens", "Ppj2b!rIMLu*@ElTCZU", "http://rolfson.net/jaden", "one"), - Account("1872745746", "Veum and Sons", "marquise@quitzonbrown.com", "owsg728", "http://fahey.name/jon_ankunding", "one"), - Account("1872745756", "Balistreri, Emard and Mayert", "verona@willmswhite.info", "wnydas6714", "http://treutelkiehn.org/marcos", "two"), - Account("1872745766", "lindkeler.net", "ed", "quia", "http://leffler.info/chaya", "one"), - Account("1872745776", "nikolaus.biz", "leonard", "oW9fdvJLkp#%I", "http://brakuswilliamson.com/bret", ""), - Account("1872745786", "bartonherzog.net", "dock@vonrueden.net", "beatae", "http://kunzeokuneva.info/shawn_langosh", "three"), - Account("1872745796", "howe.org", "chad@walker.biz", "zexfir7951", "http://crooks.com/sandrine", ""), - Account("1872745806", "shields.info", "modesto@kunzenicolas.com", "*JDSdp@VyR8f5FOALW", "http://kemmer.org/hilton", "three"), - Account("1872745816", "kihnabernathy.com", "mafalda.treutel@gislason.name", "hwuoxizq18", "http://trompbernhard.com/trea.hirthe", "two"), - Account("1872745826", "Gislason and Sons", "torrey@kshlerin.info", "OfZrTFAIq?Uyl9X$", "http://ullrich.info/carlee", "four"), - Account("1872745836", "simonis.com", "marco.cronin", "upokmxct57", "http://rippin.name/bonita_hickle", "four"), - Account("1872745856", "Howell, Beer and Yundt", "raegan@cruickshankgreenholt.org", "dHPFrtOjieum4L", "http://aufderharrunolfsdottir.info/felicia_torphy", "two"), - Account("1872745866", "Gottlieb-Ernser", "ivory.moore@paucek.com", "fugit", "http://lockmanlynch.net/alba", "four"), - Account("1872745876", "Emmerich and Sons", "lacey.bernier@hansenboyer.com", "aqzkolu6021", "http://carrollschmitt.com/willy.emard", "three"), - Account("1872745886", "Gerlach, Kirlin and Roberts", "maiya@bayergusikowski.org", "nhit3214", "http://feil.net/natasha_howe", "one"), - Account("1872745896", "ryan.net", "rubie@fahey.org", "aihw^uFgXnC%R", "http://gleasonjakubowski.biz/august", ""), - Account("1872745906", "Jewess, Wolf and Volkman", "kristin_blanda@howekuhlman.biz", "nacro5213", "http://wilkinsonleannon.name/bud.willms", "two"), - Account("1872745916", "Ritchie Group", "nathen_ortiz@turner.biz", "XfmN@G%ebsK1Jc$q", "http://price.info/urban", "two"), - Account("1872745926", "wiegand.info", "lavon_greenholt", "fzedpuq30", "http://paucekturcotte.org/kadin_gibson", ""), - Account("1872745936", "Rohan, Schneider and Daniel", "zella.effertz", "wksei21", "http://runte.com/camryn.hane", "one"), - Account("1872745946", "boyle.name", "gennaro_goldner@kovacek.biz", "eaJD#Kb6UAis@M*8jhILk", "http://kulasklein.info/nyasia", "four"), - Account("1872745956", "Pouros-Funk", "maudie@fahey.org", "wahkvms6871", "http://schaefer.info/leslie.bogan", "three"), - Account("1872745966", "Parisian-Legros", "holly", "et", "http://naderrempel.net/gwen_schmidt", "four"), - Account("1872745976", "Rosenbaum-Schulist", "jordy.krajcik", "xqzflsy843", "http://dooley.info/alek_parker", "four"), - Account("1872745986", "christiansen.info", "phoebe@larson.info", "bilvs07", "http://johns.name/columbus.dooley", "two"), - Account("1872745996", "Hauck, Thiel and VonRueden", "leif", "QVx?JvZ46e1FBmsAi", "http://bauch.org/marlin", "one"), - Account("1872746006", "Sipes and Sons", "leland", "ecgs1758", "http://dubuque.com/jacey", "one"), - Account("1872746016", "Osinski LLC", "rhoda", "nhwo705", "http://schinner.org/price", "four"), - Account("1872746026", "daniel.name", "santina@wiegand.net", "dolorem", "http://torp.net/shyanne.smitham", ""), - Account("1872746036", "darekutch.name", "ali", "U*kgl8u1p#QO9xWNnEd0b3", "http://mante.com/caie_streich", ""), - Account("1872746046", "grimes.com", "eunice_satterfield@baileymante.net", "ipsam", "http://swaniawski.org/wendell_gaylord", "three"), - Account("1872746056", "conn.name", "sandrine", "rv%XVjo#2Id?@4L", "http://rolfson.com/willy_bartell", ""), - Account("1872746066", "Kozey-Spinka", "brando.kshlerin", "consequatur", "http://collinsreichel.net/yasmine", "three"), - Account("1872746076", "Daugherty LLC", "horacio_schulist@davis.net", "sbxzn64", "http://deckow.net/roosevelt.kshlerin", "four"), - Account("1872746086", "Lubowitz LLC", "maxine@ebertmckenzie.biz", "qrcl02", "http://considineheidenreich.name/name.christiansen", ""), - Account("1872746096", "mante.name", "jayne", "xnekizj90", "http://bogisich.net/lori", "four"), - Account("1872746106", "Mante LLC", "antonio.turner@sauertorphy.com", "ckomnf175", "http://herzog.name/luigi", ""), - Account("1872746116", "Greenholt-Hodkiewicz", "moriah@mccullough.org", "udaxo7451", "http://mann.com/cecile", "three"), - Account("1872746126", "Rosenbaum, Sipes and Leffler", "keshaun@schroeder.info", "recusandae", "http://dooley.name/ewald", "two"), - Account("1872746136", "Fadel, Ferry and Kohler", "sister", "sUxoLNhl8Kty*Ve76b45G", "http://balistrerimcclure.com/jaquan_wilkinson", "two"), - Account("1872746146", "Schaden-Rosenbaum", "godfrey", "oDVcsx*m!0Rb@NjSyqdGIl", "http://pouros.net/jeremie", ""), - Account("1872746156", "Monahan, Reinger and McKenzie", "christophe.kub@luettgen.name", "fLqj&e$TyNo8gd7!", "http://keler.info/nikita.lindgren", "four"), - Account("1872746166", "bednar.info", "roselyn@hickle.com", "*2tiEP&Ic7dT", "http://jaskolski.com/conner_ortiz", "two"), - Account("1872746176", "Jewess, Wolf and Feil", "hal", "doloribus", "http://champlin.org/lue_schroeder", "three"), - Account("1872746186", "Kunze-Hettinger", "camilla_pagac", "elpbzT08Dvo6NyQF3wPEr", "http://donnellyadams.com/santino", "one"), - Account("1872746196", "Jacobs, Toy and Schultz", "billy_boehm@will.biz", "g5X*hRwlmcL6ZM", "http://larkinconsidine.org/leola", "one"), - Account("1872746206", "sanford.com", "joy@abbott.org", "rfolun872", "http://runtemoen.name/pierre", "three"), - Account("1872746216", "upton.net", "susana.gaylord", "WR4KxbU^@$Vpi%QH9Mv#T", "http://moore.info/pearl", "three"), - Account("1872746226", "wiegand.biz", "ashleigh_gutmann", "t7C&j!Ox21oha5sX*f", "http://armstronghackett.name/jaeden", "three"), - Account("1872746236", "schneider.net", "eunice.sauer@ledner.org", "U%EFVGnxw2fQ^t*", "http://schulistmetz.info/esperanza_cummerata", "two"), - Account("1872746246", "Swift-Stoltenberg", "katelin_rempel", "labore", "http://beermills.net/danielle", "two"), - Account("1872746256", "Heathcote Group", "hope.waters@parisianbruen.info", "EhG7zBTb8OseI", "http://douglas.name/porter", ""), - Account("1872746266", "hilpert.com", "phyllis.lemke", "est", "http://donnelly.com/tyrique_langosh", "one"), - Account("1872746276", "daviswolff.name", "martine@ryan.com", "incidunt", "http://schoen.info/macy", "one"), - Account("1872746286", "Bahringer, Prohaska and Mills", "merritt_reilly@lynch.info", "dyX^xZ3HTKsqFIMeA", "http://schuppe.com/rosetta.yundt", ""), - Account("1872746296", "ledner.name", "billie.lueilwitz@kertzmann.org", "Zi5K6tXh91mJG3EnjBD4r", "http://feil.com/isabelle", "four"), - Account("1872746306", "jerdecormier.com", "renee.towne@ruecker.net", "vuzoskg85", "http://mckenzie.net/zaria", ""), - Account("1872746316", "harbervandervort.org", "elta_haag@okuneva.net", "2?GVri70HkKceU*m#CZ3x", "http://whiteklocko.name/lacey.dare", "one"), - Account("1872746326", "gulgowskimann.org", "chaz_brakus", "explicabo", "http://okuneva.biz/lisandro", "two"), - Account("1872746336", "padbergconn.info", "lenore@ullrich.net", "ORrNKnhuqd7xeULa^YDk", "http://sauerkuvalis.info/braxton", "one"), - Account("1872746346", "davis.com", "margarett", "debitis", "http://spinka.info/kendra", ""), - Account("1872746366", "Gerlach Inc", "krystel_boyer", "qui", "http://pouromitham.name/efrain", "three"), - Account("1872746376", "cummerata.net", "rudy.flatley", "mzqvakic527", "http://heidenreich.net/ryann_hayes", ""), - Account("1872746386", "schowalter.name", "hyman.satterfield", "pjts564", "http://okeefedamore.biz/giovani", "one"), - Account("1872746396", "McLaughlin-Fadel", "fanny_sporer", "kyti64", "http://dickibosco.biz/zachariah", "four"), - Account("1872746406", "Gerlach-Nienow", "treva.block", "csnxhldi893", "http://kunzemurazik.net/johnny.koch", "two"), - Account("1872746416", "O'Reilly-Trantow", "grayson", "non", "http://harris.name/rosalind_marquardt", "three"), - Account("1872746426", "Larkin-Konopelski", "josianne_walker", "bwms78", "http://runolfsdottir.name/nicklaus_hayes", "two"), - Account("1872746436", "Swaniawski, Will and Gaylord", "jeramie.ohara@nader.org", "quia", "http://oreilly.info/dahlia_donnelly", ""), - Account("1872746446", "emmerichgaylord.name", "diana@hansenbeahan.net", "omnis", "http://rath.net/leif_hermann", "three"), - Account("1872746456", "armstrong.org", "genesis@rosenbaumlueilwitz.biz", "zHeu%^kxj9Y0Qr4@m*3!ov", "http://schmidtmertz.name/kira", "one"), - Account("1872746466", "Waelchi Group", "trace.heaney@heidenreichbernier.com", "whljnru03", "http://moore.biz/anibal", "two"), - Account("1872746476", "fahey.org", "ward_okuneva", "qjnz18", "http://leuschke.com/daphney", "two"), - Account("1872746486", "koelpin.info", "dylan.klocko", "vdjlot364", "http://cronin.net/cyril", "three"), - Account("1872746496", "Murphy-Breitenberg", "marcia_kreiger", "dacyz723", "http://steuber.com/ali_gibson", "three"), - Account("1872746506", "andersondicki.org", "ceasar@lind.com", "nvymdsk14", "http://kertzmann.biz/jaydon_kunze", "four"), - Account("1872746516", "watersreichel.net", "adella_price@beahanblock.biz", "maiores", "http://gutkowskirau.org/dora.williamson", "four"), - Account("1872746526", "torphy.biz", "osborne_hackett@davis.org", "wkdcu1265", "http://buckridge.net/lauretta.veum", "four"), - Account("1872746536", "Moen-Hermiston", "hildegard@hahn.com", "zbag942", "http://cummingswehner.biz/april", ""), - Account("1872746546", "Gaylord-Lowe", "jerrell", "quasi", "http://grady.biz/mohammed_brakus", ""), - Account("1872746556", "Bechtelar, Wyman and Thompson", "shanie@batz.biz", "vel", "http://gottlieb.name/elisabeth", "four"), - Account("1872746566", "jacobs.info", "lon_champlin@cristlittel.name", "aut", "http://dachgislason.org/alva", "two"), - Account("1872746576", "ankunding.com", "reina_runolfon@altenwerthhilll.net", "@g&aWsoTeJEFhHK5wr#4", "http://rice.info/giovanny_ebert", "two"), - Account("1872746586", "Okuneva-Schmitt", "esperanza@kshlerin.com", "djwhba31", "http://glovermckenzie.info/katelynn", ""), - Account("1872746596", "jones.name", "elvera", "ewoqt49", "http://sipes.com/joey.metz", "two"), - Account("1872746606", "Tromp-Roob", "brisa.mcdermott", "vcnkg254", "http://bernier.org/gage_haag", "three"), + Account(b"1872745596", b"Muller, Morar and Wisoky", b"branson_cormier", b"8jgLCzQSkB2rTZ1OtF9sNGpc", b"http://nienow.net/meagan.greenholt", b"three"), + Account(b"1872745606", b"goyette.net", b"kris_quigley@baileyjewe.biz", b"S5@3^wPv!6JsFj", b"http://bechtelar.biz/tristian.barrows", b"four"), + Account(b"1872745616", b"Ward Inc", b"angela_emard", b"zp8N@KoWyS0IYu7VR$dvBF!t", b"http://schuster.name/ashton", b"one"), + Account(b"1872745626", b"stehr.com", b"bailee_marvin@mohrlegros.net", b"cupiditate", b"http://runolfon.org/te", b"three"), + Account(b"1872745636", b"kiehn.biz", b"freda", b"et", b"http://hintzprohaska.biz/wade.fisher", b"one"), + Account(b"1872745646", b"Jacobs and Sons", b"johnnie.hand", b"gzyl6714", b"http://schultzheaney.org/arvid", b""), + Account(b"1872745656", b"Larkin, Kautzer and Wiegand", b"hilton", b"zguovmdr8703", b"http://streich.com/ramona", b"one"), + Account(b"1872745666", b"Conn Inc", b"malvina_paucek@nikolausveum.net", b"numquam", b"http://conn.net/leda", b"four"), + Account(b"1872745676", b"Block, Sanford and Connelly", b"marilie_wolff", b"zKcy?U*aCGS^gf@Z", b"http://conroy.biz/zachery", b"two"), + Account(b"1872745686", b"gradyrenner.org", b"oswald@ryan.info", b"ojgwad28", b"http://kihn.org/candice", b""), + Account(b"1872745696", b"lesch.net", b"nicholas", b"Pkc72Lmr1qwI%sNV^d4@GtX", b"http://glover.name/jerad", b"two"), + Account(b"1872745706", b"sipes.biz", b"kaitlyn.bernier@reichel.net", b"in", b"http://mayert.name/jeromy", b"two"), + Account(b"1872745716", b"Hintz-Herman", b"prince.moriette", b"0hebvIS@s^BwMc", b"http://sanfordwunsch.org/alek", b""), + Account(b"1872745726", b"Hammes-Kassulke", b"brooke@gloverhintz.net", b"paokcs08", b"http://lehner.biz/stanley.dooley", b"four"), + Account(b"1872745736", b"hermann.com", b"jasper_dickens", b"Ppj2b!rIMLu*@ElTCZU", b"http://rolfson.net/jaden", b"one"), + Account(b"1872745746", b"Veum and Sons", b"marquise@quitzonbrown.com", b"owsg728", b"http://fahey.name/jon_ankunding", b"one"), + Account(b"1872745756", b"Balistreri, Emard and Mayert", b"verona@willmswhite.info", b"wnydas6714", b"http://treutelkiehn.org/marcos", b"two"), + Account(b"1872745766", b"lindkeler.net", b"ed", b"quia", b"http://leffler.info/chaya", b"one"), + Account(b"1872745776", b"nikolaus.biz", b"leonard", b"oW9fdvJLkp#%I", b"http://brakuswilliamson.com/bret", b""), + Account(b"1872745786", b"bartonherzog.net", b"dock@vonrueden.net", b"beatae", b"http://kunzeokuneva.info/shawn_langosh", b"three"), + Account(b"1872745796", b"howe.org", b"chad@walker.biz", b"zexfir7951", b"http://crooks.com/sandrine", b""), + Account(b"1872745806", b"shields.info", b"modesto@kunzenicolas.com", b"*JDSdp@VyR8f5FOALW", b"http://kemmer.org/hilton", b"three"), + Account(b"1872745816", b"kihnabernathy.com", b"mafalda.treutel@gislason.name", b"hwuoxizq18", b"http://trompbernhard.com/trea.hirthe", b"two"), + Account(b"1872745826", b"Gislason and Sons", b"torrey@kshlerin.info", b"OfZrTFAIq?Uyl9X$", b"http://ullrich.info/carlee", b"four"), + Account(b"1872745836", b"simonis.com", b"marco.cronin", b"upokmxct57", b"http://rippin.name/bonita_hickle", b"four"), + Account(b"1872745856", b"Howell, Beer and Yundt", b"raegan@cruickshankgreenholt.org", b"dHPFrtOjieum4L", b"http://aufderharrunolfsdottir.info/felicia_torphy", b"two"), + Account(b"1872745866", b"Gottlieb-Ernser", b"ivory.moore@paucek.com", b"fugit", b"http://lockmanlynch.net/alba", b"four"), + Account(b"1872745876", b"Emmerich and Sons", b"lacey.bernier@hansenboyer.com", b"aqzkolu6021", b"http://carrollschmitt.com/willy.emard", b"three"), + Account(b"1872745886", b"Gerlach, Kirlin and Roberts", b"maiya@bayergusikowski.org", b"nhit3214", b"http://feil.net/natasha_howe", b"one"), + Account(b"1872745896", b"ryan.net", b"rubie@fahey.org", b"aihw^uFgXnC%R", b"http://gleasonjakubowski.biz/august", b""), + Account(b"1872745906", b"Jewess, Wolf and Volkman", b"kristin_blanda@howekuhlman.biz", b"nacro5213", b"http://wilkinsonleannon.name/bud.willms", b"two"), + Account(b"1872745916", b"Ritchie Group", b"nathen_ortiz@turner.biz", b"XfmN@G%ebsK1Jc$q", b"http://price.info/urban", b"two"), + Account(b"1872745926", b"wiegand.info", b"lavon_greenholt", b"fzedpuq30", b"http://paucekturcotte.org/kadin_gibson", b""), + Account(b"1872745936", b"Rohan, Schneider and Daniel", b"zella.effertz", b"wksei21", b"http://runte.com/camryn.hane", b"one"), + Account(b"1872745946", b"boyle.name", b"gennaro_goldner@kovacek.biz", b"eaJD#Kb6UAis@M*8jhILk", b"http://kulasklein.info/nyasia", b"four"), + Account(b"1872745956", b"Pouros-Funk", b"maudie@fahey.org", b"wahkvms6871", b"http://schaefer.info/leslie.bogan", b"three"), + Account(b"1872745966", b"Parisian-Legros", b"holly", b"et", b"http://naderrempel.net/gwen_schmidt", b"four"), + Account(b"1872745976", b"Rosenbaum-Schulist", b"jordy.krajcik", b"xqzflsy843", b"http://dooley.info/alek_parker", b"four"), + Account(b"1872745986", b"christiansen.info", b"phoebe@larson.info", b"bilvs07", b"http://johns.name/columbus.dooley", b"two"), + Account(b"1872745996", b"Hauck, Thiel and VonRueden", b"leif", b"QVx?JvZ46e1FBmsAi", b"http://bauch.org/marlin", b"one"), + Account(b"1872746006", b"Sipes and Sons", b"leland", b"ecgs1758", b"http://dubuque.com/jacey", b"one"), + Account(b"1872746016", b"Osinski LLC", b"rhoda", b"nhwo705", b"http://schinner.org/price", b"four"), + Account(b"1872746026", b"daniel.name", b"santina@wiegand.net", b"dolorem", b"http://torp.net/shyanne.smitham", b""), + Account(b"1872746036", b"darekutch.name", b"ali", b"U*kgl8u1p#QO9xWNnEd0b3", b"http://mante.com/caie_streich", b""), + Account(b"1872746046", b"grimes.com", b"eunice_satterfield@baileymante.net", b"ipsam", b"http://swaniawski.org/wendell_gaylord", b"three"), + Account(b"1872746056", b"conn.name", b"sandrine", b"rv%XVjo#2Id?@4L", b"http://rolfson.com/willy_bartell", b""), + Account(b"1872746066", b"Kozey-Spinka", b"brando.kshlerin", b"consequatur", b"http://collinsreichel.net/yasmine", b"three"), + Account(b"1872746076", b"Daugherty LLC", b"horacio_schulist@davis.net", b"sbxzn64", b"http://deckow.net/roosevelt.kshlerin", b"four"), + Account(b"1872746086", b"Lubowitz LLC", b"maxine@ebertmckenzie.biz", b"qrcl02", b"http://considineheidenreich.name/name.christiansen", b""), + Account(b"1872746096", b"mante.name", b"jayne", b"xnekizj90", b"http://bogisich.net/lori", b"four"), + Account(b"1872746106", b"Mante LLC", b"antonio.turner@sauertorphy.com", b"ckomnf175", b"http://herzog.name/luigi", b""), + Account(b"1872746116", b"Greenholt-Hodkiewicz", b"moriah@mccullough.org", b"udaxo7451", b"http://mann.com/cecile", b"three"), + Account(b"1872746126", b"Rosenbaum, Sipes and Leffler", b"keshaun@schroeder.info", b"recusandae", b"http://dooley.name/ewald", b"two"), + Account(b"1872746136", b"Fadel, Ferry and Kohler", b"sister", b"sUxoLNhl8Kty*Ve76b45G", b"http://balistrerimcclure.com/jaquan_wilkinson", b"two"), + Account(b"1872746146", b"Schaden-Rosenbaum", b"godfrey", b"oDVcsx*m!0Rb@NjSyqdGIl", b"http://pouros.net/jeremie", b""), + Account(b"1872746156", b"Monahan, Reinger and McKenzie", b"christophe.kub@luettgen.name", b"fLqj&e$TyNo8gd7!", b"http://keler.info/nikita.lindgren", b"four"), + Account(b"1872746166", b"bednar.info", b"roselyn@hickle.com", b"*2tiEP&Ic7dT", b"http://jaskolski.com/conner_ortiz", b"two"), + Account(b"1872746176", b"Jewess, Wolf and Feil", b"hal", b"doloribus", b"http://champlin.org/lue_schroeder", b"three"), + Account(b"1872746186", b"Kunze-Hettinger", b"camilla_pagac", b"elpbzT08Dvo6NyQF3wPEr", b"http://donnellyadams.com/santino", b"one"), + Account(b"1872746196", b"Jacobs, Toy and Schultz", b"billy_boehm@will.biz", b"g5X*hRwlmcL6ZM", b"http://larkinconsidine.org/leola", b"one"), + Account(b"1872746206", b"sanford.com", b"joy@abbott.org", b"rfolun872", b"http://runtemoen.name/pierre", b"three"), + Account(b"1872746216", b"upton.net", b"susana.gaylord", b"WR4KxbU^@$Vpi%QH9Mv#T", b"http://moore.info/pearl", b"three"), + Account(b"1872746226", b"wiegand.biz", b"ashleigh_gutmann", b"t7C&j!Ox21oha5sX*f", b"http://armstronghackett.name/jaeden", b"three"), + Account(b"1872746236", b"schneider.net", b"eunice.sauer@ledner.org", b"U%EFVGnxw2fQ^t*", b"http://schulistmetz.info/esperanza_cummerata", b"two"), + Account(b"1872746246", b"Swift-Stoltenberg", b"katelin_rempel", b"labore", b"http://beermills.net/danielle", b"two"), + Account(b"1872746256", b"Heathcote Group", b"hope.waters@parisianbruen.info", b"EhG7zBTb8OseI", b"http://douglas.name/porter", b""), + Account(b"1872746266", b"hilpert.com", b"phyllis.lemke", b"est", b"http://donnelly.com/tyrique_langosh", b"one"), + Account(b"1872746276", b"daviswolff.name", b"martine@ryan.com", b"incidunt", b"http://schoen.info/macy", b"one"), + Account(b"1872746286", b"Bahringer, Prohaska and Mills", b"merritt_reilly@lynch.info", b"dyX^xZ3HTKsqFIMeA", b"http://schuppe.com/rosetta.yundt", b""), + Account(b"1872746296", b"ledner.name", b"billie.lueilwitz@kertzmann.org", b"Zi5K6tXh91mJG3EnjBD4r", b"http://feil.com/isabelle", b"four"), + Account(b"1872746306", b"jerdecormier.com", b"renee.towne@ruecker.net", b"vuzoskg85", b"http://mckenzie.net/zaria", b""), + Account(b"1872746316", b"harbervandervort.org", b"elta_haag@okuneva.net", b"2?GVri70HkKceU*m#CZ3x", b"http://whiteklocko.name/lacey.dare", b"one"), + Account(b"1872746326", b"gulgowskimann.org", b"chaz_brakus", b"explicabo", b"http://okuneva.biz/lisandro", b"two"), + Account(b"1872746336", b"padbergconn.info", b"lenore@ullrich.net", b"ORrNKnhuqd7xeULa^YDk", b"http://sauerkuvalis.info/braxton", b"one"), + Account(b"1872746346", b"davis.com", b"margarett", b"debitis", b"http://spinka.info/kendra", b""), + Account(b"1872746366", b"Gerlach Inc", b"krystel_boyer", b"qui", b"http://pouromitham.name/efrain", b"three"), + Account(b"1872746376", b"cummerata.net", b"rudy.flatley", b"mzqvakic527", b"http://heidenreich.net/ryann_hayes", b""), + Account(b"1872746386", b"schowalter.name", b"hyman.satterfield", b"pjts564", b"http://okeefedamore.biz/giovani", b"one"), + Account(b"1872746396", b"McLaughlin-Fadel", b"fanny_sporer", b"kyti64", b"http://dickibosco.biz/zachariah", b"four"), + Account(b"1872746406", b"Gerlach-Nienow", b"treva.block", b"csnxhldi893", b"http://kunzemurazik.net/johnny.koch", b"two"), + Account(b"1872746416", b"O'Reilly-Trantow", b"grayson", b"non", b"http://harris.name/rosalind_marquardt", b"three"), + Account(b"1872746426", b"Larkin-Konopelski", b"josianne_walker", b"bwms78", b"http://runolfsdottir.name/nicklaus_hayes", b"two"), + Account(b"1872746436", b"Swaniawski, Will and Gaylord", b"jeramie.ohara@nader.org", b"quia", b"http://oreilly.info/dahlia_donnelly", b""), + Account(b"1872746446", b"emmerichgaylord.name", b"diana@hansenbeahan.net", b"omnis", b"http://rath.net/leif_hermann", b"three"), + Account(b"1872746456", b"armstrong.org", b"genesis@rosenbaumlueilwitz.biz", b"zHeu%^kxj9Y0Qr4@m*3!ov", b"http://schmidtmertz.name/kira", b"one"), + Account(b"1872746466", b"Waelchi Group", b"trace.heaney@heidenreichbernier.com", b"whljnru03", b"http://moore.biz/anibal", b"two"), + Account(b"1872746476", b"fahey.org", b"ward_okuneva", b"qjnz18", b"http://leuschke.com/daphney", b"two"), + Account(b"1872746486", b"koelpin.info", b"dylan.klocko", b"vdjlot364", b"http://cronin.net/cyril", b"three"), + Account(b"1872746496", b"Murphy-Breitenberg", b"marcia_kreiger", b"dacyz723", b"http://steuber.com/ali_gibson", b"three"), + Account(b"1872746506", b"andersondicki.org", b"ceasar@lind.com", b"nvymdsk14", b"http://kertzmann.biz/jaydon_kunze", b"four"), + Account(b"1872746516", b"watersreichel.net", b"adella_price@beahanblock.biz", b"maiores", b"http://gutkowskirau.org/dora.williamson", b"four"), + Account(b"1872746526", b"torphy.biz", b"osborne_hackett@davis.org", b"wkdcu1265", b"http://buckridge.net/lauretta.veum", b"four"), + Account(b"1872746536", b"Moen-Hermiston", b"hildegard@hahn.com", b"zbag942", b"http://cummingswehner.biz/april", b""), + Account(b"1872746546", b"Gaylord-Lowe", b"jerrell", b"quasi", b"http://grady.biz/mohammed_brakus", b""), + Account(b"1872746556", b"Bechtelar, Wyman and Thompson", b"shanie@batz.biz", b"vel", b"http://gottlieb.name/elisabeth", b"four"), + Account(b"1872746566", b"jacobs.info", b"lon_champlin@cristlittel.name", b"aut", b"http://dachgislason.org/alva", b"two"), + Account(b"1872746576", b"ankunding.com", b"reina_runolfon@altenwerthhilll.net", b"@g&aWsoTeJEFhHK5wr#4", b"http://rice.info/giovanny_ebert", b"two"), + Account(b"1872746586", b"Okuneva-Schmitt", b"esperanza@kshlerin.com", b"djwhba31", b"http://glovermckenzie.info/katelynn", b""), + Account(b"1872746596", b"jones.name", b"elvera", b"ewoqt49", b"http://sipes.com/joey.metz", b"two"), + Account(b"1872746606", b"Tromp-Roob", b"brisa.mcdermott", b"vcnkg254", b"http://bernier.org/gage_haag", b"three"), ] diff --git a/tests/test_fetcher.py b/tests/test_fetcher.py index 3fe8d19..06347c7 100644 --- a/tests/test_fetcher.py +++ b/tests/test_fetcher.py @@ -1,4 +1,5 @@ # coding: utf-8 +from base64 import b64decode import unittest import mock import lastpass @@ -12,11 +13,11 @@ def setUp(self): self.username = 'username' self.password = 'password' self.key_iteration_count = 5000 - self.hash = '7880a04588cfab954aa1a2da98fd9c0d2c6eba4c53e36a94510e6dbf30759256' + self.hash = b'7880a04588cfab954aa1a2da98fd9c0d2c6eba4c53e36a94510e6dbf30759256' self.session_id = '53ru,Hb713QnEVM5zWZ16jMvxS0' self.session = Session(self.session_id, self.key_iteration_count) self.blob_response = 'TFBBVgAAAAMxMjJQUkVNAAAACjE0MTQ5' - self.blob_bytes = self.blob_response.decode('base64') + self.blob_bytes = b64decode(self.blob_response) self.blob = Blob(self.blob_bytes, self.key_iteration_count) self.login_post_data = {'method': 'mobile', @@ -145,13 +146,13 @@ def test_fetch_raises_exception_on_http_error(self): def test_make_key_generates_correct_keys(self): keys = [ - (1, 'C/Bh2SGWxI8JDu54DbbpV8J9wa6pKbesIb9MAXkeF3Y='.decode('base64')), - (5, 'pE9goazSCRqnWwcixWM4NHJjWMvB5T15dMhe6ug1pZg='.decode('base64')), - (10, 'n9S0SyJdrMegeBHtkxUx8Lzc7wI6aGl+y3/udGmVey8='.decode('base64')), - (50, 'GwI8/kNy1NjIfe3Z0VAZfF78938UVuCi6xAL3MJBux0='.decode('base64')), - (100, 'piGdSULeHMWiBS3QJNM46M5PIYwQXA6cNS10pLB3Xf8='.decode('base64')), - (500, 'OfOUvVnQzB4v49sNh4+PdwIFb9Fr5+jVfWRTf+E2Ghg='.decode('base64')), - (1000, 'z7CdwlIkbu0XvcB7oQIpnlqwNGemdrGTBmDKnL9taPg='.decode('base64')), + (1, b64decode('C/Bh2SGWxI8JDu54DbbpV8J9wa6pKbesIb9MAXkeF3Y=')), + (5, b64decode('pE9goazSCRqnWwcixWM4NHJjWMvB5T15dMhe6ug1pZg=')), + (10, b64decode('n9S0SyJdrMegeBHtkxUx8Lzc7wI6aGl+y3/udGmVey8=')), + (50, b64decode('GwI8/kNy1NjIfe3Z0VAZfF78938UVuCi6xAL3MJBux0=')), + (100, b64decode('piGdSULeHMWiBS3QJNM46M5PIYwQXA6cNS10pLB3Xf8=')), + (500, b64decode('OfOUvVnQzB4v49sNh4+PdwIFb9Fr5+jVfWRTf+E2Ghg=')), + (1000, b64decode('z7CdwlIkbu0XvcB7oQIpnlqwNGemdrGTBmDKnL9taPg=')), ] for iterations, key in keys: @@ -159,13 +160,13 @@ def test_make_key_generates_correct_keys(self): def test_make_hash(self): hashes = [ - (1, 'a1943cfbb75e37b129bbf78b9baeab4ae6dd08225776397f66b8e0c7a913a055'), - (5, 'a95849e029a7791cfc4503eed9ec96ab8675c4a7c4e82b00553ddd179b3d8445'), - (10, '0da0b44f5e6b7306f14e92de6d629446370d05afeb1dc07cfcbe25f169170c16'), - (50, '1d5bc0d636da4ad469cefe56c42c2ff71589facb9c83f08fcf7711a7891cc159'), - (100, '82fc12024acb618878ba231a9948c49c6f46e30b5a09c11d87f6d3338babacb5'), - (500, '3139861ae962801b59fc41ff7eeb11f84ca56d810ab490f0d8c89d9d9ab07aa6'), - (1000, '03161354566c396fcd624a424164160e890e96b4b5fa6d942fc6377ab613513b'), + (1, b'a1943cfbb75e37b129bbf78b9baeab4ae6dd08225776397f66b8e0c7a913a055'), + (5, b'a95849e029a7791cfc4503eed9ec96ab8675c4a7c4e82b00553ddd179b3d8445'), + (10, b'0da0b44f5e6b7306f14e92de6d629446370d05afeb1dc07cfcbe25f169170c16'), + (50, b'1d5bc0d636da4ad469cefe56c42c2ff71589facb9c83f08fcf7711a7891cc159'), + (100, b'82fc12024acb618878ba231a9948c49c6f46e30b5a09c11d87f6d3338babacb5'), + (500, b'3139861ae962801b59fc41ff7eeb11f84ca56d810ab490f0d8c89d9d9ab07aa6'), + (1000, b'03161354566c396fcd624a424164160e890e96b4b5fa6d942fc6377ab613513b'), ] for iterations, hash in hashes: diff --git a/tests/test_parser.py b/tests/test_parser.py index 3dc9206..3b5ec41 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,6 +1,8 @@ # coding: utf-8 +from base64 import b64decode +import codecs import unittest -from StringIO import StringIO +from io import BytesIO from lastpass.blob import Blob from lastpass.chunk import Chunk from lastpass.parser import Parser @@ -12,16 +14,16 @@ def setUp(self): self.key_iteration_count = 5000 self.blob = Blob(TEST_BLOB, self.key_iteration_count) self.padding = 'BEEFFACE' - self.encryption_key = 'OfOUvVnQzB4v49sNh4+PdwIFb9Fr5+jVfWRTf+E2Ghg='.decode('base64') + self.encryption_key = b64decode('OfOUvVnQzB4v49sNh4+PdwIFb9Fr5+jVfWRTf+E2Ghg=') self.chunks = Parser.extract_chunks(self.blob) - self.accounts = [Parser.parse_account(i, self.encryption_key) for i in self.chunks['ACCT']] + self.accounts = [Parser.parse_account(i, self.encryption_key) for i in self.chunks[b'ACCT']] def test_extract_chunks_returns_chunks_as_a_dict(self): self.assertIsInstance(self.chunks, dict) def test_extract_chunks_all_keys_are_strings(self): - self.assertListEqual(self.chunks.keys(), TEST_CHUNK_IDS) + self.assertListEqual(list(self.chunks.keys()), TEST_CHUNK_IDS) def test_extract_chunks_all_values_are_arrays(self): self.assertListEqual(list(set([type(v) for v in self.chunks.values()])), [list]) @@ -36,51 +38,51 @@ def test_parse_account_parses_account(self): self.assertListEqual([a.id for a in self.accounts], [a.id for a in TEST_ACCOUNTS]) def test_read_chunk_returns_a_chunk(self): - io = StringIO(('4142434400000004DEADBEEF' + self.padding).decode('hex')) - self.assertEqual(Parser.read_chunk(io), Chunk('ABCD', 'DEADBEEF'.decode('hex'))) - self.assertEqual(io.pos, 12) + io = BytesIO(codecs.decode('4142434400000004DEADBEEF' + self.padding, 'hex_codec')) + self.assertEqual(Parser.read_chunk(io), Chunk(b'ABCD', codecs.decode('DEADBEEF', 'hex_codec'))) + self.assertEqual(io.tell(), 12) def test_read_item_returns_an_item(self): - io = StringIO(('00000004DEADBEEF' + self.padding).decode('hex')) - self.assertEqual(Parser.read_item(io), 'DEADBEEF'.decode('hex')) - self.assertEqual(io.pos, 8) + io = BytesIO(codecs.decode('00000004DEADBEEF' + self.padding, 'hex_codec')) + self.assertEqual(Parser.read_item(io), codecs.decode('DEADBEEF', 'hex_codec')) + self.assertEqual(io.tell(), 8) def test_skip_item_skips_an_empty_item(self): - io = StringIO(('00000000' + self.padding).decode('hex')) + io = BytesIO(codecs.decode('00000000' + self.padding, 'hex_codec')) Parser.skip_item(io) - self.assertEqual(io.pos, 4) + self.assertEqual(io.tell(), 4) def test_skip_item_skips_a_non_empty_item(self): - io = StringIO(('00000004DEADBEEF' + self.padding).decode('hex')) + io = BytesIO(codecs.decode('00000004DEADBEEF' + self.padding, 'hex_codec')) Parser.skip_item(io) - self.assertEqual(io.pos, 8) + self.assertEqual(io.tell(), 8) def test_read_id_returns_an_id(self): - io = StringIO('ABCD' + self.padding) - self.assertEqual(Parser.read_id(io), 'ABCD') - self.assertEqual(io.pos, 4) + io = BytesIO(('ABCD' + self.padding).encode()) + self.assertEqual(Parser.read_id(io), b'ABCD') + self.assertEqual(io.tell(), 4) def test_read_size_returns_a_size(self): - io = StringIO(('DEADBEEF' + self.padding).decode('hex')) + io = BytesIO(codecs.decode('DEADBEEF' + self.padding, 'hex_codec')) self.assertEqual(Parser.read_size(io), 0xDEADBEEF) - self.assertEqual(io.pos, 4) + self.assertEqual(io.tell(), 4) def test_read_payload_returns_a_payload(self): - io = StringIO(('FEEDDEADBEEF' + self.padding).decode('hex')) - self.assertEqual(Parser.read_payload(io, 6), 'FEEDDEADBEEF'.decode('hex')) - self.assertEqual(io.pos, 6) + io = BytesIO(codecs.decode('FEEDDEADBEEF' + self.padding, 'hex_codec')) + self.assertEqual(Parser.read_payload(io, 6), codecs.decode('FEEDDEADBEEF', 'hex_codec')) + self.assertEqual(io.tell(), 6) def test_read_uint32_returns_a_number(self): - io = StringIO(('DEADBEEF' + self.padding).decode('hex')) + io = BytesIO(codecs.decode('DEADBEEF' + self.padding, 'hex_codec')) self.assertEqual(Parser.read_size(io), 0xDEADBEEF) - self.assertEqual(io.pos, 4) + self.assertEqual(io.tell(), 4) def test_decode_hex_decodes_hex(self): - self.assertEqual(Parser.decode_hex(''), '') - self.assertEqual(Parser.decode_hex('00ff'), '\x00\xFF') - self.assertEqual(Parser.decode_hex('00010203040506070809'), '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09') - self.assertEqual(Parser.decode_hex('000102030405060708090a0b0c0d0e0f'), '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F') - self.assertEqual(Parser.decode_hex('8af633933e96a3c3550c2734bd814195'), '\x8A\xF6\x33\x93\x3E\x96\xA3\xC3\x55\x0C\x27\x34\xBD\x81\x41\x95') + self.assertEqual(Parser.decode_hex(''), b'') + self.assertEqual(Parser.decode_hex('00ff'), b'\x00\xFF') + self.assertEqual(Parser.decode_hex('00010203040506070809'), b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09') + self.assertEqual(Parser.decode_hex('000102030405060708090a0b0c0d0e0f'), b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F') + self.assertEqual(Parser.decode_hex('8af633933e96a3c3550c2734bd814195'), b'\x8A\xF6\x33\x93\x3E\x96\xA3\xC3\x55\x0C\x27\x34\xBD\x81\x41\x95') def test_decode_hex_raises_exception_on_odd_length(self): self.assertRaises(TypeError, Parser.decode_hex, '0') @@ -89,91 +91,91 @@ def test_decode_hex_raises_exception_on_invalid_characters(self): self.assertRaises(TypeError, Parser.decode_hex, 'xz') def test_decode_base64_decodes_base64(self): - self.assertEqual(Parser.decode_base64(''), '') - self.assertEqual(Parser.decode_base64('YQ=='), 'a') - self.assertEqual(Parser.decode_base64('YWI='), 'ab') - self.assertEqual(Parser.decode_base64('YWJj'), 'abc') - self.assertEqual(Parser.decode_base64('YWJjZA=='), 'abcd') + self.assertEqual(Parser.decode_base64(''), b'') + self.assertEqual(Parser.decode_base64('YQ=='), b'a') + self.assertEqual(Parser.decode_base64('YWI='), b'ab') + self.assertEqual(Parser.decode_base64('YWJj'), b'abc') + self.assertEqual(Parser.decode_base64('YWJjZA=='), b'abcd') def test_decode_aes256_auto_decodes_a_blank_string(self): - self.assertEqual(Parser.decode_aes256_auto('', self.encryption_key), '') + self.assertEqual(Parser.decode_aes256_auto('', self.encryption_key), b'') def test_decode_aes256_auto_decodes_ecb_plain_string(self): self.assertEqual(Parser.decode_aes256_auto( - 'BNhd3Q3ZVODxk9c0C788NUPTIfYnZuxXfkghtMJ8jVM='.decode('base64'), self.encryption_key), - 'All your base are belong to us') + b64decode('BNhd3Q3ZVODxk9c0C788NUPTIfYnZuxXfkghtMJ8jVM='), self.encryption_key), + b'All your base are belong to us') def test_decode_aes256_auto_decodes_ecb_base64_string(self): self.assertEqual(Parser.decode_aes256_auto( 'BNhd3Q3ZVODxk9c0C788NUPTIfYnZuxXfkghtMJ8jVM=', self.encryption_key), - 'All your base are belong to us') + b'All your base are belong to us') def test_decode_aes256_auto_decodes_cbc_plain_string(self): self.assertEqual(Parser.decode_aes256_auto( - 'IcokDWmjOkKtLpZehWKL6666Uj6fNXPpX6lLWlou+1Lrwb+D3ymP6BAwd6C0TB3hSA=='.decode('base64'), self.encryption_key), - 'All your base are belong to us') + b64decode('IcokDWmjOkKtLpZehWKL6666Uj6fNXPpX6lLWlou+1Lrwb+D3ymP6BAwd6C0TB3hSA=='), self.encryption_key), + b'All your base are belong to us') def test_decode_aes256_auto_decodes_cbc_base64_string(self): self.assertEqual(Parser.decode_aes256_auto( '!YFuiAVZgOD2K+s6y8yaMOw==|TZ1+if9ofqRKTatyUaOnfudletslMJ/RZyUwJuR/+aI=', self.encryption_key), - 'All your base are belong to us') + b'All your base are belong to us') def test_decode_aes256_ecb_plain_decodes_a_blank_string(self): self.assertEqual(Parser.decode_aes256_ecb_plain( - ''.decode('base64'), self.encryption_key), - '') + b64decode(''), self.encryption_key), + b'') def test_decode_aes256_ecb_plain_decodes_a_short_string(self): self.assertEqual(Parser.decode_aes256_ecb_plain( - '8mHxIA8rul6eq72a/Gq2iw=='.decode('base64'), self.encryption_key), - '0123456789') + b64decode('8mHxIA8rul6eq72a/Gq2iw=='), self.encryption_key), + b'0123456789') def test_decode_aes256_ecb_plain_decodes_a_long_string(self): self.assertEqual(Parser.decode_aes256_ecb_plain( - 'BNhd3Q3ZVODxk9c0C788NUPTIfYnZuxXfkghtMJ8jVM='.decode('base64'), self.encryption_key), - 'All your base are belong to us') + b64decode('BNhd3Q3ZVODxk9c0C788NUPTIfYnZuxXfkghtMJ8jVM='), self.encryption_key), + b'All your base are belong to us') def test_decode_aes256_ecb_base64_decodes_a_blank_string(self): self.assertEqual(Parser.decode_aes256_ecb_base64( '', self.encryption_key), - '') + b'') def test_decode_aes256_ecb_base64_decodes_a_short_string(self): self.assertEqual(Parser.decode_aes256_ecb_base64( '8mHxIA8rul6eq72a/Gq2iw==', self.encryption_key), - '0123456789') + b'0123456789') def test_decode_aes256_ecb_base64_decodes_a_long_string(self): self.assertEqual(Parser.decode_aes256_ecb_base64( 'BNhd3Q3ZVODxk9c0C788NUPTIfYnZuxXfkghtMJ8jVM=', self.encryption_key), - 'All your base are belong to us') + b'All your base are belong to us') def test_decode_aes256_cbc_plain_decodes_a_blank_string(self): self.assertEqual(Parser.decode_aes256_cbc_plain( - ''.decode('base64'), self.encryption_key), - '') + b64decode(''), self.encryption_key), + b'') def test_decode_aes256_cbc_plain_decodes_a_short_string(self): self.assertEqual(Parser.decode_aes256_cbc_plain( - 'IQ+hiIy0vGG4srsHmXChe3ehWc/rYPnfiyqOG8h78DdX'.decode('base64'), self.encryption_key), - '0123456789') + b64decode('IQ+hiIy0vGG4srsHmXChe3ehWc/rYPnfiyqOG8h78DdX'), self.encryption_key), + b'0123456789') def test_decode_aes256_cbc_plain_decodes_a_long_string(self): self.assertEqual(Parser.decode_aes256_cbc_plain( - 'IcokDWmjOkKtLpZehWKL6666Uj6fNXPpX6lLWlou+1Lrwb+D3ymP6BAwd6C0TB3hSA=='.decode('base64'), self.encryption_key), - 'All your base are belong to us') + b64decode('IcokDWmjOkKtLpZehWKL6666Uj6fNXPpX6lLWlou+1Lrwb+D3ymP6BAwd6C0TB3hSA=='), self.encryption_key), + b'All your base are belong to us') def test_decode_aes256_cbc_base64_decodes_a_blank_string(self): self.assertEqual(Parser.decode_aes256_cbc_base64( '', self.encryption_key), - '') + b'') def test_decode_aes256_cbc_base64_decodes_a_short_string(self): self.assertEqual(Parser.decode_aes256_cbc_base64( '!6TZb9bbrqpocMaNgFjrhjw==|f7RcJ7UowesqGk+um+P5ug==', self.encryption_key), - '0123456789') + b'0123456789') def test_decode_aes256_cbc_base64_decodes_a_long_string(self): self.assertEqual(Parser.decode_aes256_cbc_base64( '!YFuiAVZgOD2K+s6y8yaMOw==|TZ1+if9ofqRKTatyUaOnfudletslMJ/RZyUwJuR/+aI=', self.encryption_key), - 'All your base are belong to us') + b'All your base are belong to us')