"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/lib/datahandler/pwsafe.py" between
revelation-0.5.3.tar.xz and revelation-0.5.4.tar.xz

About: Revelation is a password manager for the GNOME 3 desktop.

pwsafe.py  (revelation-0.5.3.tar.xz):pwsafe.py  (revelation-0.5.4.tar.xz)
skipping to change at line 27 skipping to change at line 27
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# #
from . import base from . import base
from revelation import data, entry, util from revelation import data, entry
import locale, re, struct import locale, re, struct
from Cryptodome.Cipher import Blowfish from Cryptodome.Cipher import Blowfish
import Cryptodome.Random as Random
FIELDTYPE_NAME = 0x00 FIELDTYPE_NAME = 0x00
FIELDTYPE_UUID = 0x01 FIELDTYPE_UUID = 0x01
FIELDTYPE_GROUP = 0x02 FIELDTYPE_GROUP = 0x02
FIELDTYPE_TITLE = 0x03 FIELDTYPE_TITLE = 0x03
FIELDTYPE_USER = 0x04 FIELDTYPE_USER = 0x04
FIELDTYPE_NOTES = 0x05 FIELDTYPE_NOTES = 0x05
FIELDTYPE_PASSWORD = 0x06 FIELDTYPE_PASSWORD = 0x06
FIELDTYPE_END = 0xff FIELDTYPE_END = 0xff
skipping to change at line 62 skipping to change at line 63
] ]
def __init__(self, input = None): def __init__(self, input = None):
self.count = [0, 0] self.count = [0, 0]
self.init() self.init()
if input != None: if input != None:
self.update(input) self.update(input)
def __bytelist2longBigEndian(self, list): def __bytelist2longBigEndian(self, list):
imax = len(list)/4 imax = len(list)//4
hl = [0] * imax hl = [0] * imax
j = 0 j = 0
i = 0 i = 0
while i < imax: while i < imax:
b0 = long(ord(list[j])) << 24 b0 = list[j] << 24
b1 = long(ord(list[j+1])) << 16 b1 = list[j+1] << 16
b2 = long(ord(list[j+2])) << 8 b2 = list[j+2] << 8
b3 = long(ord(list[j+3])) b3 = list[j+3]
hl[i] = b0 | b1 | b2 | b3 hl[i] = b0 | b1 | b2 | b3
i = i+1 i = i+1
j = j+4 j = j+4
return hl return hl
def __long2bytesBigEndian(self, n, blocksize=0): def __long2bytesBigEndian(self, n, blocksize=0):
s = '' s = b''
pack = struct.pack pack = struct.pack
while n > 0: while n > 0:
s = pack('>I', n & 0xffffffff) + s s = pack('>I', n & 0xffffffff) + s
n = n >> 32 n = n >> 32
for i in range(len(s)): for i in range(len(s)):
if s[i] != '\000': if s[i] != b'\000':
break break
else: else:
s = '\000' s = b'\000'
i = 0 i = 0
s = s[i:] s = s[i:]
if blocksize > 0 and len(s) % blocksize: if blocksize > 0 and len(s) % blocksize:
s = (blocksize - len(s) % blocksize) * '\000' + s s = (blocksize - len(s) % blocksize) * b'\000' + s
return s return s
def __rotateLeft(self, x, n): def __rotateLeft(self, x, n):
return (x << n) | (x >> (32-n)) return (x << n) | (x >> (32-n))
def __transform(self, W): def __transform(self, W):
for t in range(16, 80): for t in range(16, 80):
W.append(self.__rotateLeft( W.append(self.__rotateLeft(
W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) & 0xffffffff) W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) & 0xffffffff)
skipping to change at line 167 skipping to change at line 168
input = [] + self.input input = [] + self.input
count = [] + self.count count = [] + self.count
index = (self.count[1] >> 3) & 0x3f index = (self.count[1] >> 3) & 0x3f
if index < 56: if index < 56:
padLen = 56 - index padLen = 56 - index
else: else:
padLen = 120 - index padLen = 120 - index
padding = ['\200'] + ['\000'] * 63 padding = [128] + [0] * 63
self.update(padding[:padLen]) self.update(padding[:padLen])
bits = self.__bytelist2longBigEndian(self.input[:56]) + count bits = self.__bytelist2longBigEndian(self.input[:56]) + count
self.__transform(bits) self.__transform(bits)
digest = self.__long2bytesBigEndian(self.H0, 4) + \ digest = self.__long2bytesBigEndian(self.H0, 4) + \
self.__long2bytesBigEndian(self.H1, 4) + \ self.__long2bytesBigEndian(self.H1, 4) + \
self.__long2bytesBigEndian(self.H2, 4) + \ self.__long2bytesBigEndian(self.H2, 4) + \
self.__long2bytesBigEndian(self.H3, 4) + \ self.__long2bytesBigEndian(self.H3, 4) + \
self.__long2bytesBigEndian(self.H4, 4) self.__long2bytesBigEndian(self.H4, 4)
skipping to change at line 204 skipping to change at line 204
self.length = 0 self.length = 0
self.input = [] self.input = []
self.H0 = H0 self.H0 = H0
self.H1 = H1 self.H1 = H1
self.H2 = H2 self.H2 = H2
self.H3 = H3 self.H3 = H3
self.H4 = H4 self.H4 = H4
def update(self, inBuf): def update(self, inBuf):
leninBuf = long(len(inBuf)) leninBuf = len(inBuf)
index = (self.count[1] >> 3) & 0x3F index = (self.count[1] >> 3) & 0x3F
self.count[1] = self.count[1] + (leninBuf << 3) self.count[1] = self.count[1] + (leninBuf << 3)
if self.count[1] < (leninBuf << 3): if self.count[1] < (leninBuf << 3):
self.count[0] = self.count[0] + 1 self.count[0] = self.count[0] + 1
self.count[0] = self.count[0] + (leninBuf >> 29) self.count[0] = self.count[0] + (leninBuf >> 29)
partLen = 64 - index partLen = 64 - index
skipping to change at line 235 skipping to change at line 235
i = 0 i = 0
self.input = self.input + list(inBuf) self.input = self.input + list(inBuf)
# misc functions common to both datahandlers # misc functions common to both datahandlers
def decrypt(key, ciphertext, iv = None): def decrypt(key, ciphertext, iv = None):
"Decrypts data" "Decrypts data"
if len(ciphertext) % 8 != 0: if len(ciphertext) % 8 != 0:
raise base.FormatError raise base.FormatError
cipher = Blowfish.new(key) cipher = Blowfish.new(key, Blowfish.MODE_ECB)
cbc = iv cbc = iv
plaintext = "" plaintext = b""
for cipherblock in [ ciphertext[i * 8 : (i + 1) * 8] for i in range(len(ciph ertext) / 8) ]: for cipherblock in [ ciphertext[i * 8 : (i + 1) * 8] for i in range(len(ciph ertext) // 8) ]:
plainblock = decrypt_block(cipher, cipherblock) plainblock = decrypt_block(cipher, cipherblock)
if cbc != None: if cbc != None:
plainblock = "".join([ chr(ord(plainblock[i]) ^ ord(cbc[i])) for i i n range(len(plainblock)) ]) plainblock = bytes([ plainblock[i] ^ cbc[i] for i in range(len(plain block)) ])
cbc = cipherblock cbc = cipherblock
plaintext += plainblock plaintext += plainblock
return plaintext return plaintext
def decrypt_block(cipher, block): def decrypt_block(cipher, block):
"Decrypts a block with the given cipher" "Decrypts a block with the given cipher"
block = block[3] + block[2] + block[1] + block[0] + block[7] + block[6] + bl ock[5] + block[4] block = bytes((block[3], block[2], block[1], block[0], block[7], block[6], b lock[5], block[4]))
block = cipher.decrypt(block) block = cipher.decrypt(block)
block = block[3] + block[2] + block[1] + block[0] + block[7] + block[6] + bl ock[5] + block[4] block = bytes((block[3], block[2], block[1], block[0], block[7], block[6], b lock[5], block[4]))
return block return block
def encrypt(key, plaintext, iv = None): def encrypt(key, plaintext, iv = None):
"Encrypts data" "Encrypts data"
if len(plaintext) % 8 != 0: if len(plaintext) % 8 != 0:
raise base.FormatError raise base.FormatError
cipher = Blowfish.new(key) cipher = Blowfish.new(key, Blowfish.MODE_ECB)
cbc = iv cbc = iv
ciphertext = "" ciphertext = b""
for plainblock in [ plaintext[i * 8 : (i + 1) * 8] for i in range(len(plaint ext) / 8) ]: for plainblock in [ plaintext[i * 8 : (i + 1) * 8] for i in range(len(plaint ext) // 8) ]:
if cbc != None: if cbc != None:
plainblock = "".join([ chr(ord(plainblock[i]) ^ ord(cbc[i])) for i i n range(len(plainblock)) ]) plainblock = bytes([ plainblock[i] ^ cbc[i] for i in range(len(plain block)) ])
cipherblock = encrypt_block(cipher, plainblock) cipherblock = encrypt_block(cipher, plainblock)
ciphertext += cipherblock ciphertext += cipherblock
if cbc != None: if cbc != None:
cbc = cipherblock cbc = cipherblock
return ciphertext return ciphertext
def encrypt_block(cipher, block): def encrypt_block(cipher, block):
"Encrypts a block with the given cipher" "Encrypts a block with the given cipher"
block = block[3] + block[2] + block[1] + block[0] + block[7] + block[6] + bl ock[5] + block[4] block = bytes((block[3], block[2], block[1], block[0], block[7], block[6], b lock[5], block[4]))
block = cipher.encrypt(block) block = cipher.encrypt(block)
block = block[3] + block[2] + block[1] + block[0] + block[7] + block[6] + bl ock[5] + block[4] block = bytes((block[3], block[2], block[1], block[0], block[7], block[6], b lock[5], block[4]))
return block return block
def generate_testhash(password, random): def generate_testhash(password, random):
"Generates a testhash based on a password and a random string" "Generates a testhash based on a password and a random string"
key = SHA(random + "\x00\x00" + password).digest() key = SHA(random + b"\x00\x00" + password.encode()).digest()
cipher = Blowfish.new(key) cipher = Blowfish.new(key, Blowfish.MODE_ECB)
for i in range(1000): for i in range(1000):
random = encrypt_block(cipher, random) random = encrypt_block(cipher, random)
h = SHA() h = SHA()
h.init(0, 0, 0, 0, 0) h.init(0, 0, 0, 0, 0)
h.update(random) h.update(random)
h.update("\x00\x00") h.update(b"\x00\x00")
testhash = h.digest() testhash = h.digest()
return testhash return testhash
def create_field(value, type = FIELDTYPE_NAME): def create_field(value, type = FIELDTYPE_NAME):
"Creates a field" "Creates a field"
if isinstance(value, str):
value = value.encode()
field = struct.pack("ii", len(value), type) + value field = struct.pack("ii", len(value), type) + value
if len(value) == 0 or len(value) % 8 != 0: if len(value) == 0 or len(value) % 8 != 0:
field += "\x00" * (8 - len(value) % 8) field += b"\x00" * (8 - len(value) % 8)
return field return field
def normalize_field(field): def normalize_field(field):
"Normalizes a field value" "Normalizes a field value"
enc = locale.getpreferredencoding() enc = locale.getpreferredencoding()
field = field.replace("\x00", "") field = field.replace(b"\x00", b"")
field = re.sub("\s+", " ", field) field = re.sub(b"\s+", b" ", field)
field = field.strip() field = field.strip()
field = field.decode(enc, "replace") field = field.decode(enc, "replace")
field = field.encode("utf-8", "replace")
return field return field
def parse_field_header(header): def parse_field_header(header):
"Parses field data, returns the length and type" "Parses field data, returns the length and type"
if len(header) < 8: if len(header) < 8:
raise base.FormatError raise base.FormatError
length, type = struct.unpack("ii", header[:8]) length, type = struct.unpack("ii", header[:8])
skipping to change at line 373 skipping to change at line 374
raise base.FormatError raise base.FormatError
if (len(input) - 56) % 8 != 0: if (len(input) - 56) % 8 != 0:
raise base.FormatError raise base.FormatError
def export_data(self, entrystore, password): def export_data(self, entrystore, password):
"Exports data from an entrystore" "Exports data from an entrystore"
# serialize data # serialize data
enc = locale.getpreferredencoding() enc = locale.getpreferredencoding()
db = "" db = b""
iter = entrystore.iter_children(None) iter = entrystore.iter_children(None)
while iter is not None: while iter is not None:
e = entrystore.get_entry(iter) e = entrystore.get_entry(iter)
if type(e) != entry.FolderEntry: if type(e) != entry.FolderEntry:
e = e.convert_generic() e = e.convert_generic()
edata = "" edata = b""
edata += create_field(e.name.encode(enc, "replace") + "\xAD" + e edata += create_field(e.name.encode(enc, "replace") + b"\xAD" +
[entry.UsernameField].encode("iso-8859-1")) e[entry.UsernameField].encode("iso-8859-1"))
edata += create_field(e[entry.PasswordField].encode(enc, "replac e")) edata += create_field(e[entry.PasswordField].encode(enc, "replac e"))
edata += create_field(e.description.encode(enc, "replace")) edata += create_field(e.description.encode(enc, "replace"))
db += edata db += edata
iter = entrystore.iter_traverse_next(iter) iter = entrystore.iter_traverse_next(iter)
# encrypt data # encrypt data
random = util.random_string(8) rand = Random.new()
salt = util.random_string(20) random = rand.read(8)
iv = util.random_string(8) salt = rand.read(20)
iv = rand.read(8)
testhash = generate_testhash(password, random) testhash = generate_testhash(password, random)
ciphertext = encrypt(SHA(password + salt).digest(), db, iv) ciphertext = encrypt(SHA(password.encode() + salt).digest(), db, iv)
return random + testhash + salt + iv + ciphertext return random + testhash + salt + iv + ciphertext
def import_data(self, input, password): def import_data(self, input, password):
"Imports data into an entrystore" "Imports data into an entrystore"
# read header and test password # read header and test password
if password is None: if password is None:
raise base.PasswordError raise base.PasswordError
random = input[0:8] random = input[0:8]
testhash = input[8:28] testhash = input[8:28]
salt = input[28:48] salt = input[28:48]
iv = input[48:56] iv = input[48:56]
if testhash != generate_testhash(password, random): if testhash != generate_testhash(password, random):
raise base.PasswordError raise base.PasswordError
# load data # load data
db = decrypt(SHA(password + salt).digest(), input[56:], iv) db = decrypt(SHA(password.encode() + salt).digest(), input[56:], iv )
entrystore = data.EntryStore() entrystore = data.EntryStore()
while len(db) > 0: while len(db) > 0:
dbentry = { "name" : "", "username" : "", "password" : "", "note" : "" } dbentry = { "name" : "", "username" : "", "password" : "", "note" : "" }
for f in ( "name", "password", "note" ): for f in ( "name", "password", "note" ):
flen, ftype = parse_field_header(db[:8]) flen, ftype = parse_field_header(db[:8])
value = db[8:8 + flen] value = db[8:8 + flen]
if f == "name" and "\xAD" in value: if f == "name" and b"\xAD" in value:
value, dbentry["username"] = value.split("\xAD", 1) value, dbentry["username"] = value.split(b"\xAD", 1)
dbentry[f] = value dbentry[f] = value
db = db[8 + flen:] db = db[8 + flen:]
e = entry.GenericEntry() e = entry.GenericEntry()
e.name = normalize_field(dbentry["name"]) e.name = normalize_field(dbentry["name"])
e.description = normalize_field(dbentry["note"]) e.description = normalize_field(dbentry["note"])
e[entry.UsernameField] = normalize_field(dbentry["username"]) e[entry.UsernameField] = normalize_field(dbentry["username"])
e[entry.PasswordField] = normalize_field(dbentry["password"]) e[entry.PasswordField] = normalize_field(dbentry["password"])
skipping to change at line 511 skipping to change at line 513
if len(input) < 56: if len(input) < 56:
raise base.FormatError raise base.FormatError
if (len(input) - 56) % 8 != 0: if (len(input) - 56) % 8 != 0:
raise base.FormatError raise base.FormatError
def export_data(self, entrystore, password): def export_data(self, entrystore, password):
"Exports data from an entrystore" "Exports data from an entrystore"
# set up magic entry at start of database # set up magic entry at start of database
db = "" db = b""
db += "\x48\x00\x00\x00\x00\x00\x00\x00" db += b"\x48\x00\x00\x00\x00\x00\x00\x00"
db += " !!!Version 2 File Format!!! Please upgrade to PasswordSafe 2.0 o db += b" !!!Version 2 File Format!!! Please upgrade to PasswordSafe 2.0
r later" or later"
db += "\x03\x00\x00\x00\x06\x00\x00\x00" db += b"\x03\x00\x00\x00\x06\x00\x00\x00"
db += "2.0\x00\x00\x00\x00\x00" db += b"2.0\x00\x00\x00\x00\x00"
db += "\x00\x00\x00\x00\x06\x00\x00\x00" db += b"\x00\x00\x00\x00\x06\x00\x00\x00"
db += "\x00\x00\x00\x00\x00\x00\x00\x00" db += b"\x00\x00\x00\x00\x00\x00\x00\x00"
# serialize data # serialize data
uuids = [] uuids = []
iter = entrystore.iter_children(None) iter = entrystore.iter_children(None)
enc = locale.getpreferredencoding() enc = locale.getpreferredencoding()
rand = Random.new()
while iter is not None: while iter is not None:
e = entrystore.get_entry(iter) e = entrystore.get_entry(iter)
if type(e) != entry.FolderEntry: if type(e) != entry.FolderEntry:
e = e.convert_generic() e = e.convert_generic()
uuid = util.random_string(16) uuid = rand.read(16)
while uuid in uuids: while uuid in uuids:
uuid = util.random_string(16) uuid = rand.read(16)
edata = "" edata = b""
edata += create_field(uuid, FIELDTYPE_UUID) edata += create_field(uuid, FIELDTYPE_UUID)
edata += create_field(self.__get_group(entrystore, iter), FIELDT YPE_GROUP) edata += create_field(self.__get_group(entrystore, iter), FIELDT YPE_GROUP)
edata += create_field(e.name.encode(enc, "replace"), FIELDTYPE_T ITLE) edata += create_field(e.name.encode(enc, "replace"), FIELDTYPE_T ITLE)
s = e[entry.UsernameField] s = e[entry.UsernameField]
if s is None: if s is None:
s = "" s = ""
edata += create_field(s.encode(enc, "replace"), FIELDTYPE_USER) edata += create_field(s.encode(enc, "replace"), FIELDTYPE_USER)
edata += create_field(e[entry.PasswordField].encode(enc, "replac e"), FIELDTYPE_PASSWORD) edata += create_field(e[entry.PasswordField].encode(enc, "replac e"), FIELDTYPE_PASSWORD)
edata += create_field(e.description.encode(enc, "replace"), FIEL DTYPE_NOTES) edata += create_field(e.description.encode(enc, "replace"), FIEL DTYPE_NOTES)
edata += create_field("", FIELDTYPE_END) edata += create_field("", FIELDTYPE_END)
db += edata db += edata
iter = entrystore.iter_traverse_next(iter) iter = entrystore.iter_traverse_next(iter)
# encrypt data # encrypt data
random = util.random_string(8) rand = Random.new()
salt = util.random_string(20) random = rand.read(8)
iv = util.random_string(8) salt = rand.read(20)
iv = rand.read(8)
testhash = generate_testhash(password, random) testhash = generate_testhash(password, random)
ciphertext = encrypt(SHA(password + salt).digest(), db, iv) ciphertext = encrypt(SHA(password.encode() + salt).digest(), db, iv)
return random + testhash + salt + iv + ciphertext return random + testhash + salt + iv + ciphertext
def import_data(self, input, password): def import_data(self, input, password):
"Imports data into an entrystore" "Imports data into an entrystore"
# read header and test password # read header and test password
if password is None: if password is None:
raise base.PasswordError raise base.PasswordError
random = input[0:8] random = input[0:8]
testhash = input[8:28] testhash = input[8:28]
salt = input[28:48] salt = input[28:48]
iv = input[48:56] iv = input[48:56]
if testhash != generate_testhash(password, random): if testhash != generate_testhash(password, random):
raise base.PasswordError raise base.PasswordError
# load data # load data
db = decrypt(SHA(password + salt).digest(), input[56:], iv) db = decrypt(SHA(password.encode() + salt).digest(), input[56:], iv )
entrystore = data.EntryStore() entrystore = data.EntryStore()
# read magic entry # read magic entry
for f in "magic", "version", "prefs": for f in "magic", "version", "prefs":
flen, ftype = parse_field_header(db) flen, ftype = parse_field_header(db)
value = db[8:8 + flen] value = db[8:8 + flen]
if f == "magic" and value != " !!!Version 2 File Format!!! Please up grade to PasswordSafe 2.0 or later": if f == "magic" and value != b" !!!Version 2 File Format!!! Please u pgrade to PasswordSafe 2.0 or later":
raise base.FormatError raise base.FormatError
db = db[8 + flen:] db = db[8 + flen:]
# import entries # import entries
e = entry.GenericEntry() e = entry.GenericEntry()
group = None group = None
groupmap = {} groupmap = {}
while len(db) > 0: while len(db) > 0:
flen, ftype = parse_field_header(db) flen, ftype = parse_field_header(db)
value = normalize_field(db[8:8 + flen]) value = normalize_field(db[8:8 + flen])
if ftype == FIELDTYPE_NAME: if ftype == FIELDTYPE_NAME:
if "\xAD" not in value: if b"\xAD" not in value:
e.name = value e.name = value
else: else:
n, u = value.split("\xAD", 1) n, u = value.split(b"\xAD", 1)
e.name = normalize_field(n) e.name = normalize_field(n)
e[entry.UsernameField] = normalize_field(u) e[entry.UsernameField] = normalize_field(u)
elif ftype == FIELDTYPE_TITLE: elif ftype == FIELDTYPE_TITLE:
e.name = value e.name = value
elif ftype == FIELDTYPE_USER: elif ftype == FIELDTYPE_USER:
e[entry.UsernameField] = value e[entry.UsernameField] = value
 End of changes. 46 change blocks. 
62 lines changed or deleted 66 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)