def input23(prompt=u''): # Py2-vs-Py3: """ Utility function to bridge Py2 and Py3 incompatibilities. Maps Py2 raw_input() to input() for Py2. Py2: raw_input() Py3: input() """ if sys.version_info[0] < 3: # Py2-vs-Py3: return normalize_nfc(raw_input(prompt)) else: return normalize_nfc(input(prompt))
def editPassword(self, item): row = self.passwordTable.row(item) group = self.pwMap.groups[self.selectedGroup] try: decrypted = self.cachedOrDecryptPassword(row) decryptedComments = self.cachedOrDecryptComments(row) except CallException: return dialog = AddPasswordDialog(self.pwMap.trezor, self.settings) entry = group.entry(row) dialog.keyEdit.setText(encoding.normalize_nfc(entry[0])) dialog.pwEdit1.setText(encoding.normalize_nfc(decrypted)) dialog.pwEdit2.setText(encoding.normalize_nfc(decrypted)) doc = QTextDocument(encoding.normalize_nfc(decryptedComments), parent=self) dialog.commentsEdit.setDocument(doc) if not dialog.exec_(): return item = QTableWidgetItem(dialog.key()) pwItem = QTableWidgetItem("*****") commentsItem = QTableWidgetItem("*****") self.passwordTable.setItem(row, self.KEY_IDX, item) self.passwordTable.setItem(row, self.PASSWORD_IDX, pwItem) self.passwordTable.setItem(row, self.COMMENTS_IDX, commentsItem) plainPw = dialog.pw1() plainComments = dialog.comments() if len(plainPw) + len(plainComments) > basics.MAX_SIZE_OF_PASSWDANDCOMMENTS: self.settings.mlogger.log("Password and/or comments too long. " "Combined they must not be larger than %d." % basics.MAX_SIZE_OF_PASSWDANDCOMMENTS, logging.CRITICAL, "User IO") return plainPwComments = ("%4d" % len(plainPw)) + plainPw + plainComments encPw = self.pwMap.encryptPassword(plainPwComments, self.selectedGroup) bkupPw = self.pwMap.backupKey.encryptPassword(plainPwComments) group.updateEntry(row, dialog.key(), encPw, bkupPw) self.cachePasswordComments(row, plainPw, plainComments) self.setModified(True) self.settings.mlogger.log("Password and comments entry " "for '%s', row '%d' was edited." % (dialog.key(), row), logging.DEBUG, "GUI IO")
def obfuscateFilename(self, plaintextFileName): """ Plaintext filename -> pad16 -> AES encrypt on Trezor --> base64 encode --> homegrown padding == obfuscated filename input: string output: string """ namebytes = encoding.tobytes(plaintextFileName) pad16 = Padding(BLOCKSIZE).pad(namebytes) # self.settings.mlogger.log("Press confirm on Trezor device to encrypt file " # "name %s (if necessary).", plaintextFileName) # we do not use an IV here so that we can quickly deobfuscate # filenames without having to read the file encFn = self.trezor.encrypt_keyvalue( basics.Magic.fileNameNode, basics.Magic.fileNameKey, pad16, ask_on_encrypt=False, ask_on_decrypt=not self.settings.QArg, iv=self.baddressIv) bs64 = base64.urlsafe_b64encode(encFn) # mod 4 bs64Str = encoding.normalize_nfc(bs64) ret = PaddingHomegrown().pad(bs64Str) # mod 16 self.settings.mlogger.log( "The obfuscated filename for \"%s\" is \"%s\"." % (plaintextFileName, ret), logging.DEBUG, "Encryption") # self.settings.mlogger.log("\n\tplaintext is %s (%d), \n\tpad16 is %s (%d), " # "\n\tencFn is %s (%d), \n\tbs64 is %s (%d), \n\thgP is %s (%d)", # plaintextFileName, len(plaintextFileName), pad16, len(pad16), # binascii.hexlify(encFn), len(encFn), bs64, len(bs64), ret, len(ret)) return ret
def loadPasswords(self, item): """ Slot that should load items for group that has been clicked on. """ self.passwordTable.clear() # clears cahce, but also clears the header, the 3 titles headerKey = QTableWidgetItem(u"Key") headerValue = QTableWidgetItem(u"Password/Value") headerComments = QTableWidgetItem(u"Comments") self.passwordTable.setColumnCount(self.NO_OF_PASSWDTABLE_COLUMNS) self.passwordTable.setHorizontalHeaderItem(self.KEY_IDX, headerKey) self.passwordTable.setHorizontalHeaderItem(self.PASSWORD_IDX, headerValue) self.passwordTable.setHorizontalHeaderItem(self.COMMENTS_IDX, headerComments) groupName = encoding.normalize_nfc(item.text()) self.selectedGroup = groupName group = self.pwMap.groups[groupName] self.passwordTable.setRowCount(len(group.entries)) i = 0 for key, encValue, bkupValue in group.entries: item = QTableWidgetItem(key) pwItem = QTableWidgetItem("*****") commentsItem = QTableWidgetItem("*****") self.passwordTable.setItem(i, self.KEY_IDX, item) self.passwordTable.setItem(i, self.PASSWORD_IDX, pwItem) self.passwordTable.setItem(i, self.COMMENTS_IDX, commentsItem) i = i+1 self.passwordTable.resizeRowsToContents() self.passwordTable.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.passwordTable.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents) self.passwordTable.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents) self.settings.mlogger.log("Loaded password group '%s'." % (groupName), logging.DEBUG, "GUI IO")
def callback_PassphraseRequest(self, msg): if self.passphrase is not None: return proto.PassphraseAck(passphrase=str(self.passphrase)) if self.readpassphrasefromstdin: # read passphrase from stdin try: passphrase = getpass.getpass(u"Please enter passphrase: ") passphrase = encoding.normalize_nfc(passphrase) except KeyboardInterrupt: sys.stderr.write( u"\nKeyboard interrupt: passphrase not read. Aborting.\n") sys.exit(3) except Exception as e: sys.stderr.write( u"Critical error: Passphrase not read. Aborting. (%s)" % e) sys.exit(3) else: dialog = TrezorPassphraseDialog() if not dialog.exec_(): sys.exit(3) else: passphrase = dialog.passphrase() return proto.PassphraseAck(passphrase=passphrase)
def decryptPassword(self, encryptedPassword, groupName): """ Decrypt a password. First block is IV. After decryption strips PKCS#5 padding. @param groupName key that will be shown to user on Trezor and was used to encrypt the password. A string in utf-8. @returns: string in unicode """ ugroup = tobytes(groupName) iv, encryptedPassword = encryptedPassword[: BLOCKSIZE], encryptedPassword[ BLOCKSIZE:] # we junk the input, decrypt and reassemble the plaintext passwordBytes = b"" first = not self.settings.NArg splits = [ encryptedPassword[x:x + self.MAX_PADDED_TREZOR_ENCRYPT_SIZE] for x in range(0, len(encryptedPassword), self.MAX_PADDED_TREZOR_ENCRYPT_SIZE) ] for junk in splits: plain = self.trezor.decrypt_keyvalue(basics.Magic.groupNode, ugroup, junk, ask_on_encrypt=False, ask_on_decrypt=first, iv=iv) first = False passwordBytes += Padding(BLOCKSIZE).unpad(plain) return normalize_nfc(passwordBytes)
def comments(self): doc = self.commentsEdit.document().toPlainText() if doc is None: doc = u'' else: doc = encoding.normalize_nfc(doc) return doc
def encryptOnTrezorDevice(self, blob, keystring, innerIv=None): """ Encrypt data. Does PKCS#5 padding before encryption. Store IV as first block. @param keystring: key that will be shown to user on Trezor and used to encrypt the data. A string in utf-8 @returns: bytes """ self.settings.mlogger.log( 'Time entering encryptOnTrezorDevice: %s' % (datetime.datetime.now()), logging.DEBUG, "Encryption") rnd = Random.new() rndBlock = rnd.read(BLOCKSIZE) if innerIv is not None: rndBlock = innerIv ukeystring = encoding.normalize_nfc( keystring) # keystring.decode("utf-8") # minimum size of unpadded plaintext as input to trezor.encrypt_keyvalue() is 0 ==> padded that is 16 bytes # maximum size of unpadded plaintext as input to trezor.encrypt_keyvalue() is 1023 ==> padded that is 1024 bytes # plaintext input to trezor.encrypt_keyvalue() must be a multiple of 16 # trezor.encrypt_keyvalue() throws error on anythin larger than 1024 # In order to handle blobs larger than 1023 we junk the blobs encrypted = b'' first = not self.settings.QArg splits = [ blob[x:x + self.MAXUNPADDEDTREZORENCRYPTSIZE] for x in range(0, len(blob), self.MAXUNPADDEDTREZORENCRYPTSIZE) ] curr, max = 0, len(splits) for junk in splits: padded = Padding(BLOCKSIZE).pad(junk) try: encrypted += self.trezor.encrypt_keyvalue( basics.Magic.levelTwoNode, ukeystring, padded, ask_on_encrypt=False, ask_on_decrypt=first, iv=rndBlock) except Exception as e: self.settings.mlogger.log('Trezor failed. (%s)' % (e), logging.CRITICAL, "Encryption") raise first = False curr += 1 if self.logger.getEffectiveLevel() == logging.DEBUG: sys.stderr.write("\rencrypting block %d of %d" % (curr, max), ) if self.logger.getEffectiveLevel() == logging.DEBUG: sys.stderr.write(" --> done\n") ret = rndBlock + encrypted self.settings.mlogger.log( "Trezor encryption: plain-size = %d, encrypted-size = %d" % (len(blob), len(ret)), logging.DEBUG, "Encryption") self.settings.mlogger.log( 'Time leaving encryptOnTrezorDevice: %s' % (datetime.datetime.now()), logging.DEBUG, "Encryption") return ret
def __str__(self): try: ss = u'[\n' for ee in self.entries: ss += u'(\t%s\n\t%s\n\t%s\n)' % (normalize_nfc( ee[0]), binascii.hexlify(ee[1]), binascii.hexlify(ee[2])) return (ss + u'\n]') except Exception: ss = u'[\n' mydict = self.__dict__ for instvarkey in mydict: instvarval = mydict[instvarkey] for ee in instvarval: ss += u'(\t%s\n\t%s\n\t%s\n)' % (normalize_nfc( ee[0]), normalize_nfc(binascii.hexlify( ee[1])), normalize_nfc(binascii.hexlify(ee[2]))) return (ss + u'\n]')
def prefillPassphrase(self, passphrase): """ Instead of asking for passphrase, use this one """ if passphrase is not None: self.passphrase = encoding.normalize_nfc(passphrase) else: self.passphrase = None
def generatePassword(self): trezor_entropy = self.trezor.get_entropy(32) urandom_entropy = os.urandom(32) passwdBytes = hashlib.sha256(trezor_entropy + urandom_entropy).digest() # base85 encoding not yet implemented in Python 2.7, (requires Python 3+) # so we use base64 encoding # remove the base64 buffer char =, remove easily confused chars 0 and O, as well as I and l passwdB64bytes = base64.urlsafe_b64encode(passwdBytes) passwdB64bytes.replace(b'=', '') passwdB64bytes.replace(b'0', '') passwdB64bytes.replace(b'O', '') passwdB64bytes.replace(b'I', '') passwdB64bytes.replace(b'l', '') # print "bin =", passwdBin, ", base =", passwdB64, " binlen =", len(passwdBin), "baselen =", len(passwdB64) # instead of setting the values, we concatenate them to the existing values # This way, by clicking the "Generate password" button one can create an arbitrary long random password. self.pwEdit1.setText(self.pw1() + encoding.normalize_nfc(passwdB64bytes)) self.pwEdit2.setText(self.pw2() + encoding.normalize_nfc(passwdB64bytes))
def setSelectedFile(self, fileNames): """ Takes a py list as input and concatenates it and then places it into the single-line text field """ filenamesconcat = "" for file in fileNames: head, tail = os.path.split(file) filenamesconcat += '"' + tail + '"' + ' ' self.selectedFileEdit.setText(encoding.normalize_nfc(filenamesconcat))
def importCsv(self): """ Read a properly formated CSV file from disk and add its contents to the current entries. Import format in CSV should be : group, key, password, comments There is no error checking, so be extra careful. Make a backup first. Entries from CSV will be *added* to existing pwdb. If this is not desired create an empty pwdb file first. GroupNames are unique, so if a groupname exists then key-password-comments tuples are added to the already existing group. If a group name does not exist, a new group is created and the key-password-comments tuples are added to the newly created group. Keys are not unique. So key-password-comments are always added. If a key with a given name existed before and the CSV file contains a key with the same name, then the key-password-comments is added and after the import the given group has 2 keys with the same name. Both keys exist then, the old from before the import, and the new one from the import. Examples of valid CSV file format: Some example lines First Bank account,login,myloginname, # no comment [email protected],2-factor-authentication key,abcdef12345678,seed to regenerate 2FA codes # with comment [email protected],recovery phrase,"passwd with 2 commas , ,", # with comma [email protected],large multi-line comments,,"first line, some comma, second line" """ if self.modified: self.saveDatabase() copyfile(self.settings.dbFilename, self.settings.dbFilename + ".beforeCsvImport.backup") self.settings.mlogger.log("WARNING: You are about to import entries from a " "CSV file into your current password-database file. For safety " "reasons please make a backup copy now.\nFurthermore, this" "operation can be slow, so please be patient.", logging.NOTSET, "CSV import") dialog = QFileDialog(self, "Select CSV file to import", "", "CSV files (*.csv)") dialog.setAcceptMode(QFileDialog.AcceptOpen) res = dialog.exec_() if not res: return fname = encoding.normalize_nfc(dialog.selectedFiles()[0]) listOfAddedGroupNames = self.pwMap.importCsv(fname) for groupNameNew in listOfAddedGroupNames: item = QStandardItem(groupNameNew) self.groupsModel.appendRow(item) self.groupsTree.sortByColumn(0, Qt.AscendingOrder) self.setModified(True)
def migrateToUnicode(groupsDictBytes, settings): """ In an old version of TrezorPass groupName and key (0-element of tuple) were in Py2 type 'str' (not in 'unicode') and did not allow foreign characters. To fix this when we read the pwdb file we must migrate from Py2 'str' to 'unicode'. Takes an instance such as PasswordMap.groups which is a dictionary of PasswordGroup instances. Each PasswordGroup instance is an object with entries which are lists of tuples. Migrated unpickled pwdb data of groups from Py2 to Py3 format. Takes an unpickled pwdb groups object in Py2 and returns an unpickled pwdb groups object in Py3 format. @params groupsDictBytes: un unpickled password groups instance @type groupsDictBytes: a bytes (Py3) or str (py2) version of unpickled dictionary {} of PasswordGroup instances @returns: a dictionary {} of PasswordGroup instances with groupName and key in str (Py3) or unicode (Py2) """ groupsDictStr = {} # dictionary of PasswordGroup() instances ii = 0 for groupName in groupsDictBytes: ii += 1 # groupName is of type bytes pwGroup = groupsDictBytes[groupName] # pwGroup is of type PasswordGroup # create and add a new empty group in Py3, convert groupname to type string. groupsDictStr[normalize_nfc(groupName)] = PasswordGroup() # settings.mlogger.log("Password group %d read: \n%s" % (ii, normalize_nfc(groupName)), # logging.DEBUG, "TrezorPass database migration") for entry in pwGroup.entries: key, encPw, bkupPw = entry # add tuple to Py3 group groupsDictStr[normalize_nfc(groupName)].addEntry( normalize_nfc(key), encPw, bkupPw) settings.mlogger.log( "%d password groups have been migrated " "to unicode." % (ii), logging.DEBUG, "TrezorPass database migration") return (groupsDictStr)
def deleteGroup(self, item): # without checking user groupName = encoding.normalize_nfc(item.text()) self.selectedGroup = None del self.pwMap.groups[groupName] itemIdx = self.groupsModel.indexFromItem(item) self.groupsModel.takeRow(itemIdx.row()) self.passwordTable.setRowCount(0) self.groupsTree.clearSelection() self.setModified(True) self.settings.mlogger.log("Group '%s' was deleted." % (groupName), logging.DEBUG, "GUI IO")
def editGroupWithCheck(self, item): """ Slot to edit name a password group. """ groupNameOld = encoding.normalize_nfc(item.text()) dialog = AddGroupDialog(self.pwMap.groups, self.settings) dialog.setWindowTitle("Edit group name") dialog.groupNameLabel.setText("New name for group") dialog.setNewGroupName(groupNameOld) if not dialog.exec_(): return groupNameNew = dialog.newGroupName() self.editGroup(item, groupNameOld, groupNameNew)
def safetyCheckEncrypt(plaintextFname, encryptedFname, fileMap, settings): """ check if previous encryption worked by renaming plaintextFname file to plaintextFname.<random number>.org decrypting file named encryptedFname producing new file decryptedFname comparing/diffing decryptedFname to original file now named plaintextFname.<random number>.org removing decrypted file decryptedFname renaming original file plaintextFname.<random number>.org back to input plaintextFname @param plaintextFname: name of existing plaintext file whose previous encryption needs to be double-checked @type plaintextFname: C{string} @param encryptedFname: name of existing encrypted file (i.e. the plaintext file encrypted) @type encryptedFname: C{string} @param settings: holds settings for how to log info/warnings/errors @type settings: L{Settings} @param fileMap: object to use to handle file format of encrypted file @type fileMap: L{file_map.FileMap} @returns: True if safety check passes successfuly, False otherwise @rtype: C{bool} """ urandom_entropy = os.urandom(64) randomBytes = hashlib.sha256(urandom_entropy).digest() # randomBytes is bytes in Py3, str in Py2 # base85 encoding not yet implemented in Python 2.7, (requires Python 3+) # so we use base64 encoding # replace the base64 buffer char = randomB64bytes = base64.urlsafe_b64encode(randomBytes) # randomB64bytes is bytes in Py3, str in Py2 randomB64str = encoding.normalize_nfc(randomB64bytes) randomB64str = randomB64str.replace(u'=', u'-') originalFname = plaintextFname + u"." + randomB64str + u".orignal" os.rename(plaintextFname, originalFname) decryptedFname = decryptFile(encryptedFname, settings, fileMap) aresame = filecmp.cmp(decryptedFname, originalFname, shallow=False) ihead, itail = os.path.split(plaintextFname) ohead, otail = os.path.split(encryptedFname) if aresame: settings.mlogger.log( "Safety check of file \"%s\" (\"%s\") was successful." % (otail, itail), logging.INFO, "File encryption") else: settings.mlogger.log( "Fatal error: Safety check of file \"%s\" (\"%s\") failed! " "You must inestigate. Encryption was flawed!" % (otail, itail), logging.CRITICAL, "File encryption") os.remove(decryptedFname) os.rename(originalFname, plaintextFname) return aresame
def decryptPassword(self, encryptedPassword, privateKey): """ Decrypt RSA-OAEP encrypted password. @param encryptedPassword: encrypted password entry, key/value pair @type encryptedPassword: bytes @returns: decrypted pasword of type string (unicode) """ cipher = PKCS1_OAEP.new(privateKey) passwordBytes = b'' splits = [ encryptedPassword[x:x + self.RSA_BLOCKSIZE] for x in range(0, len(encryptedPassword), self.RSA_BLOCKSIZE) ] for junk in splits: passwordBytes += cipher.decrypt(junk) return (encoding.normalize_nfc(passwordBytes))
def __init__(self, logger=None, mlogger=None): """ @param logger: holds logger for where to log info/warnings/errors If None, a default logger will be created. @type logger: L{logging.Logger} @param mlogger: holds mlogger for where to log info/warnings/errors If None, a default mlogger will be created. @type mlogger: L{utils.MLogger} """ super(Settings, self).__init__(logger, mlogger) self.dbFilename = None # pwdb file name self.qsettings = QSettings(u"ConstructibleUniverse", u"TrezorPass") fname = self.qsettings.value(u"database/filename") # returns None or unicode string if fname is not None: self.dbFilename = encoding.normalize_nfc(fname) self.TArg = False # Terminal mode self.NArg = False # noconfirm mode self.PArg = None # passphrase self.QArg = False # set pwdb file as Defaut self.FArg = None # pwdb file name, None or a string self.AArg = False # add flag self.CArg = False # clip flag self.SArg = False # show self.OArg = False # showcomments self.BArg = False # showboth self.EArg = False # showkeys self.XArg = False # showgroups self.DArg = False # delete self.RArg = False # rename group self.UArg = False # update password entries self.YArg = None # importcsv self.ZArg = None # exportcsv self.WArg = None # password/secret, None or a string self.MArg = None # comments, None or a string self.GArg = None # group name, None or a string self.KArg = None # key name, None or a string self.n0Arg = None # new group name, None or a string self.n1Arg = None # new key name, None or a string self.n2Arg = None # new password, None or a string self.n3Arg = None # new comments, None or a string
def deobfuscateFilename(self, obfuscatedFileName): """ obfuscated filename --> homegrown unpadding --> base64 decode --> AES decrypt on Trezor --> unpad16 == Plaintext filename input: string output: string """ hgUp = PaddingHomegrown().unpad(obfuscatedFileName) # mod 4 hgUpBytes = encoding.tobytes(hgUp) bs64 = base64.urlsafe_b64decode(hgUpBytes) # mod anything self.settings.mlogger.log( "Press confirm on Trezor device to decrypt " "file name %s (if necessary)." % (obfuscatedFileName), logging.DEBUG, "Encryption") if len(bs64) % BLOCKSIZE != 0: raise ValueError("Critical error. File name " + obfuscatedFileName + " could not be deobfuscated. Skipping it.") # we do not use an IV here so that we can quickly deobfuscate filenames # without reading the file, even for files that do not exist decFn = self.trezor.decrypt_keyvalue( basics.Magic.fileNameNode, basics.Magic.fileNameKey, bs64, ask_on_encrypt=False, ask_on_decrypt=not self.settings.QArg, iv=self.baddressIv) ret = Padding(BLOCKSIZE).unpad(decFn) retStr = encoding.normalize_nfc(ret) # self.settings.mlogger.log("\n\tobfuscatedFileName is %s (%d), " # "\n\thgUp is %s (%d), \n\tbs64 is %s (%d), \n\tdecFn is %s (%d), " # "\n\tret is %s (%d)", obfuscatedFileName, len(obfuscatedFileName), # hgUp, len(hgUp), binascii.hexlify(bs64), len(bs64), # decFn, len(decFn), ret, len(ret)) if len(retStr) == 0: # try the old version, maybe it is an old file return (self.deobfuscateFilenameOld(obfuscatedFileName)) self.settings.mlogger.log( "The plaintext filename for %s is %s." % (obfuscatedFileName, retStr), logging.DEBUG, "Encryption") return retStr
def exportCsv(self): """ Uses backup key encrypted by Trezor to decrypt all passwords at once and export them into a single paintext CSV file. Export format is CSV: group, key, password, comments """ self.settings.mlogger.log("WARNING: During backup/export all passwords will be " "written in plaintext to disk. If possible you should consider performing this " "operation on an offline or air-gapped computer. Be aware of the risks.", logging.NOTSET, "CSV export") dialog = QFileDialog(self, "Select backup export file", "", "CSV files (*.csv)") dialog.setAcceptMode(QFileDialog.AcceptSave) res = dialog.exec_() if not res: return fname = encoding.normalize_nfc(dialog.selectedFiles()[0]) self.pwMap.exportCsv(fname)
def setPw2(self, pw): self.masterEdit2.setText(encoding.normalize_nfc(pw))
def pin(self): return encoding.normalize_nfc(self.pinEdit.text())
def passphrase(self): return encoding.normalize_nfc(self.passphraseEdit.text())
def parseArgs(self, argv, settings=None, logger=None): """ Parse the command line arguments and store the results in `settings`. Report errors to `logger`. This method should be implemented in the subclass. Calling this method is only useful when the application has exactly the 3 arguments -h -v -l [level] @param settings: place to store settings; if None the default settings from the Args class will be used. So, usually this argument would be None. @type settings: L{Settings} @param logger: holds logger for where to log info/warnings/errors if None the default logger from the Args class will be used. So, usually this argument would be None. @type logger: L{logging.Logger} """ # get defaults if logger is None: logger = self.logger if settings is None: settings = self.settings try: opts, args = getopt.getopt(argv, "vhl:", ["version", "help", "logging="]) except getopt.GetoptError as e: msgBox = QMessageBox(QMessageBox.Critical, u"Wrong arguments", u"Error: %s" % e) msgBox.exec_() logger.critical(u'Wrong arguments. Error: %s.', e) sys.exit(2) loglevelused = False for opt, arg in opts: arg = normalize_nfc(arg) if opt in ("-h", "--help"): self.printUsage() sys.exit() elif opt in ("-v", "--version"): self.printVersion() sys.exit() elif opt in ("-l", "--logging"): loglevelarg = arg loglevelused = True if loglevelused: try: loglevel = int(loglevelarg) except Exception: self.settings.mlogger.log( u"Logging level not specified correctly. " "Must be integer between 1 and 5. (%s)" % loglevelarg, logging.CRITICAL, "Wrong arguments", True, logger) sys.exit(18) if loglevel > 5 or loglevel < 1: self.settings.mlogger.log( u"Logging level not specified correctly. " "Must be integer between 1 and 5. (%s)" % loglevelarg, logging.CRITICAL, "Wrong arguments", True, logger) sys.exit(19) settings.LArg = loglevel * 10 # https://docs.python.org/2/library/logging.html#levels logger.setLevel(settings.LArg) self.settings.mlogger.log( u"%s Version: %s (%s)" % (basics.NAME, basics.VERSION_STR, basics.VERSION_DATE_STR), logging.INFO, "Version", True, logger) self.settings.mlogger.log( u"Python: %s" % sys.version.replace(" \n", "; "), logging.INFO, "Version", True, logger) self.settings.mlogger.log(u"Qt Version: %s" % QT_VERSION_STR, logging.INFO, "Version", True, logger) self.settings.mlogger.log(u"PyQt Version: %s" % PYQT_VERSION_STR, logging.INFO, "Version", True, logger) self.settings.mlogger.log( u'Logging level set to %s (%d).' % (logging.getLevelName(settings.LArg), settings.LArg), logging.INFO, "Logging", True, logger) self.settings.mlogger.log(settings, logging.DEBUG, "Settings", True, logger)
def importCsv(self, fname): """ See also the method with the same name in Dialog class in dialogs.py @param fname: name of CSV file @type fname: string @returns list of srings reflecting the list of NEW group names that were ADDED to the known group names by reading from the CSV file """ if not os.path.isfile(fname): raise KeyError('File "%s" does not exist, is not a proper file, ' 'or is a directory. Aborting.' % fname) if not os.access(fname, os.R_OK): raise KeyError('File "%s" is not readable. Aborting.' % fname) # list to track all new group names read from the CSV file, # that did not exist before listOfAddedGroupNames = [] with open(fname, "r") as f: csv.register_dialect("escaped", doublequote=False, escapechar='\\') reader = csv.reader(f, dialect="escaped") for csvEntry in reader: # self.settings.mlogger.log("CSV Entry: len=%d 0=%s" % (len(csvEntry), csvEntry[0]), # logging.DEBUG, "CSV import") # self.settings.mlogger.log("CSV Entry: 0=%s, 1=%s, 2=%s, 3=%s" % # (csvEntry[0], csvEntry[1], csvEntry[2], csvEntry[3]), logging.DEBUG, # "CSV import") try: groupName = normalize_nfc(csvEntry[0]) key = normalize_nfc(csvEntry[1]) plainPw = normalize_nfc(csvEntry[2]) plainComments = normalize_nfc(csvEntry[3]) except Exception as e: raise IOError( "Critical Error: Could not import CSV file. " "CSV Entry: len=%d (should be 4) element[0]=%s. (%s)" % (len(csvEntry), csvEntry[0], e)) groupNames = self.groups.keys() if groupName not in groupNames: # groups are unique self.addGroup(groupName) listOfAddedGroupNames.append(groupName) if len(plainPw) + len( plainComments) > basics.MAX_SIZE_OF_PASSWDANDCOMMENTS: self.settings.mlogger.log( "Password and/or comments too long. " "Combined they must not be larger than %d." % basics.MAX_SIZE_OF_PASSWDANDCOMMENTS, logging.NOTSET, "CSV import") return group = self.groups[groupName] plainPwComments = ("%4d" % len(plainPw)) + plainPw + plainComments encPw = self.encryptPassword(plainPwComments, groupName) bkupPw = self.backupKey.encryptPassword(plainPwComments) group.addEntry( key, encPw, bkupPw ) # keys are not unique, multiple items with same key are allowed self.settings.mlogger.log( "TrezorPass has finished importing CSV file " "from \"%s\" into \"%s\"." % (fname, self.settings.dbFilename), logging.INFO, "CSV import") return (listOfAddedGroupNames)
def key(self): return encoding.normalize_nfc(self.keyEdit.text())
def pw1(self): return encoding.normalize_nfc(self.pwEdit1.text())
def pw2(self): return encoding.normalize_nfc(self.masterEdit2.text())
def pwFile(self): return encoding.normalize_nfc(self.pwFileEdit.text())