Commits

Martin Tournoij committed 42f8db9

Welcome to v1.1

Comments (0)

Files changed (5)

+syntax: glob
+
+*.pyc
 #!/usr/bin/env python
 #
-# Marin Tournoij <martin@arp242>
-# Free for any use, there are no restrictions
+# Copyright © 2011-2012, Martin Tournoij <martin@arp242>
+# MIT licence applies: http://opensource.org/licenses/MIT
+# http://code.arp242.net/operapass
 #
 # This just "dumps" the data to the standard output
 #
 	passwords = operapass.GetPasswordsDict(pwfile)
 
 	for p in passwords:
-		print '-' * 70
-		print p['url']
+		print('-' * 70)
+		print(p['url'])
 		for k, v in p['fields'].iteritems():
-			print '\t%s: %s' % (k, v)
-		print '-' * 70
-		print ''
+			print('\t%s: %s' % (k, v))
+		print('-' * 70)
+		print('')
 
-	print ''
-	print 'Total of %s passwords' % len(passwords)
+	print('')
+	print('Total of %s passwords' % len(passwords))
 #!/usr/bin/env python
 #
-# Marin Tournoij <martin@arp242>
-# Free for any use, there are no restrictions
+# Copyright © 2011-2012, Martin Tournoij <martin@arp242>
+# MIT licence applies: http://opensource.org/licenses/MIT
+# http://code.arp242.net/operapass
 #
 # Very basic TKinter GUI
 #
 try:
 	import Tkinter as tk
 except ImportError:
-	print "TKinter is required, but wasn't found."
-	print 'You can always use operapass-dump from the terminal.'
+	print("TKinter is required, but wasn't found.")
+	print('You can always use operapass-dump from the terminal.')
 	sys.exit(1)
 
 from operapass import operapass

operapass/operapass.py

 #!/usr/bin/env python
 #
-# Marin Tournoij <martin@arp242>
-# Free for any use, there are no restrictions
+# Copyright © 2011-2012, Martin Tournoij <martin@arp242>
+# MIT licence applies: http://opensource.org/licenses/MIT
+# http://code.arp242.net/operapass
 #
 # This is, in part, based on the information found here:
 #   http://securityxploded.com/operapasswordsecrets.php
 	import M2Crypto
 	fastdes = True
 except ImportError:
-	import pyDes
-	print "M2Crypto module not found, falling back to pyDes. Note this is *much* slower!"
+	#import pyDes
+	from . import pyDes
+	print('M2Crypto module not found, falling back to pyDes.')
+	print('Note this is *much* slower and may take up to a minute or more!')
 	fastdes = False
 
 def DecryptBlock(key, text):
 	# Static salt
-	salt = '\x83\x7D\xFC\x0F\x8E\xB3\xE8\x69\x73\xAF\xFF'
+	salt = b'\x83\x7D\xFC\x0F\x8E\xB3\xE8\x69\x73\xAF\xFF'
 
 	# Master password notes:
 	#
 	# This *only* encrypts pasword fields, not username/etc.  fields.
 	# According to http://nontroppo.org/test/Op7/FAQ/opera-users.html#wand-security
-	# if you do use a master password, the used password is a combination of the
+	# "if you do use a master password, the used password is a combination of the
 	# master password and a 128-byte random portion created at the same time.
 	# This random portion is stored outside wand.dat, also encrypted with the
-	# master password.
+	# master password."
 	# Random portion mentioned seems to be opcert6.dat
 	#
 	# According to http://my.opera.com/community/forums/topic.dml?id=132880
-	# opcert6.dat contains all private keys you have created and the associated
+	# "opcert6.dat contains all private keys you have created and the associated
 	# client certificates you have requested and installed. The private keys are
 	# protected by the security password. [...] A small block of data in the
 	# opcert6.dat file is also used when you secure the wand and mail passwords
-	# with the security password.
+	# with the security password."
 
-	t = hashlib.md5(salt + key).digest()
+	h = hashlib.md5(salt + key).digest()
 	h2 = hashlib.md5(h + salt + key).digest()
 
 	key = h[:16] + h2[:8]
 		return M2Crypto.EVP.Cipher(alg='des_ede3_cbc', key=key, iv=iv, op=0,
 			padding=0).update(text)
 	else:
-		print pyDes.triple_des(key, pyDes.CBC, iv).decrypt(text)
 		return pyDes.triple_des(key, pyDes.CBC, iv).decrypt(text)
 
 def GetPrintable(text):
+	# TODO: Exclude characters which are not printable, instead of the other way
+	# around!
 	printable = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
 	return ''.join([ b for b in text if b in printable ])
 
 					#fp.seek(fp.tell() - diff)
 					data = ('\x00' * diff) + data
 				else:
-					#print 'ret at line 57'
-					#print fp.tell()
+					#print('ret at line 57')
+					#print(fp.tell())
 					return ret
 
 			try:
 				size_key = struct.unpack('>I', fp.read(4))[0]
 				key = struct.unpack('>%ss' % size_key, fp.read(size_key))[0]
 				size_data = struct.unpack('>I', fp.read(4))[0]
-				#print hex(size_data)
+				#print(hex(size_data))
 				data = struct.unpack('>%ss' % size_data, fp.read(size_data))[0]
 
 				ret.append([key, data])
 			except:
-				#print 'passing...', fp.tell()
+				#print('passing...', fp.tell())
 				fp.seek(before)
 				pass
 
 			n = []
 			while True:
 				d = fp.read(1)
-				#print hex(ord(d)),
+				#print(hex(ord(d)), end='')
 
 				if not d:
-					#print 'ret at line 80'
-					#print fp.tell()
+					#print('ret at line 80')
+					#print(fp.tell())
 					return ret
 				n.append(d)
 
 						return ret
 					fp.seek(pos)
 					#if (ord(check[3:4]) == 8):
-					#print '__', ord(check[0:1]), '__',
-					#print '__', ord(check[1:2]), '__',
-					#print '__', ord(check[2:3]), '__',
-					#print '__', ord(check[3:4]), '__'
+					#print('__', ord(check[0:1]), '__', end='')
+					#print('__', ord(check[1:2]), '__', end='')
+					#print('__', ord(check[2:3]), '__', end='')
+					#print('__', ord(check[3:4]), '__')
 					if (ord(check[0:1]) == 0 and ord(check[1:2]) == 0
 							and ord(check[2:3]) == 0 and ord(check[3:4]) == 8):
+						
+						n = [ chr(ord(a)) for a in n ]
 						data = ''.join(n[-4:])
-						#print 'BR %s, %s\n' % (fp.tell(), len(ret))
+						#print('BR %s, %s\n' % (fp.tell(), len(ret)))
 						break
 
 def GetPasswordfile():
 			pwfile = os.path.expanduser('~/.opera/wand.dat')
 
 	if not os.path.exists(pwfile):
-		print "Password file %s doesn't exist." % pwfile
+		print("Password file %s doesn't exist." % pwfile)
 		sys.exit(1)
 
 	return pwfile
 	row = []
 	for key, d in data:
 		block = DecryptBlock(key, d)
-		# Strip non-printable characters
-		# XXX This also strips non-ASCII characters
 		block = GetPrintable(block)
 
 		# A new "row" is indicated by a timestamp
 
 		if dt:
 			if len(row) > 2:
-				if len(row) % 2 == 0:
+				if len(row) % 2 == 1:
 					del row[1]
-				rows.append(row)
+
+				if len(row) > 2:
+					rows.append(row[:-1])
 			row = []
 		else:
 			if block[:5] == '*http':
 
 	if len(row) % 2 == 0:
 		del row[1]
-	rows.append(row)
 
+
+	if len(row) > 5:
+		rows.append(row[:-1])
 	rows = rows[1:]
-	#rows = sorted(rows, key=lambda r: r[0])
+
+	i = 0
+	for row in rows:
+		if 'HTTP Authentication' in row:
+			rows[i] = [
+				row[0],
+				'HTTP Authentication', 'True',
+				'Username', row[3],
+				'Password', row[4],
+			]
+		i += 1
 
 	return rows
 
 
 setup(
 	name = 'operapass',
-	version = '1.0',
+	version = '1.1',
 	author = 'Martin Tournoij',
 	author_email = 'martin@arp242.net',
-	url = 'http://code.google.com/p/operapass/',
+	url = 'http://code.arp242.net/operapass',
 	scripts = ('operapass-dump', 'operapass-tk'),
 	packages = ('operapass',),
 	#data_files = (),