"Fossies" - the Fresh Open Source Software Archive

Member "revelation-0.5.4/src/lib/datahandler/gpass.py" (4 Oct 2020, 11498 Bytes) of package /linux/privat/revelation-0.5.4.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "gpass.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.5.3_vs_0.5.4.

    1 #
    2 # Revelation - a password manager for GNOME 2
    3 # http://oss.codepoet.no/revelation/
    4 # $Id: gpass.py 602 2007-01-03 08:06:28Z erikg $
    5 #
    6 # Module for handling GPass data
    7 #
    8 #
    9 # Copyright (c) 2003-2006 Erik Grinaker
   10 #
   11 # This program is free software; you can redistribute it and/or
   12 # modify it under the terms of the GNU General Public License
   13 # as published by the Free Software Foundation; either version 2
   14 # of the License, or (at your option) any later version.
   15 #
   16 # This program is distributed in the hope that it will be useful,
   17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   19 # GNU General Public License for more details.
   20 #
   21 # You should have received a copy of the GNU General Public License
   22 # along with this program; if not, write to the Free Software
   23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   24 #
   25 
   26 from . import base
   27 from revelation import data, entry
   28 
   29 import locale, re
   30 from Cryptodome.Cipher import Blowfish
   31 from Cryptodome.Hash import SHA
   32 
   33 
   34 IV  = b"\x05\x17\x01\x7b\x0c\x03\x36\x5e"
   35 
   36 
   37 def decrypt(ciphertext, password, magic = None):
   38     "Decrypts a data stream"
   39 
   40     # decrypt data
   41     if len(ciphertext) % 8 != 0:
   42         raise base.FormatError
   43 
   44     key     = SHA.new(password.encode()).digest()
   45     cipher      = Blowfish.new(key, Blowfish.MODE_CBC, IV)
   46 
   47     plaintext   = cipher.decrypt(ciphertext)
   48 
   49     # check magic string
   50     if magic != None:
   51         if plaintext[:len(magic)].decode() != magic:
   52             raise base.PasswordError
   53 
   54         else:
   55             plaintext = plaintext[len(magic):]
   56 
   57     # remove padding
   58     padchar = plaintext[-1]
   59     npadchar = padchar
   60 
   61     if (npadchar > 0):
   62         if plaintext[-npadchar:] != bytes([padchar] * npadchar):
   63             raise base.FormatError
   64 
   65         plaintext = plaintext[:-npadchar]
   66 
   67     return plaintext.decode()
   68 
   69 
   70 def encrypt(plaintext, password):
   71     "Encrypts a data stream"
   72 
   73     # right-pad data
   74     padlen = 8 - len(plaintext) % 8
   75 
   76     if padlen == 0:
   77         padlen = 8
   78 
   79     plaintext += bytes([padlen] * padlen)
   80 
   81     # encrypt data
   82     key = SHA.new(password).digest()
   83     cipher  = Blowfish.new(key, Blowfish.MODE_CBC, IV)
   84 
   85     return cipher.encrypt(plaintext)
   86 
   87 
   88 
   89 class GPass04(base.DataHandler):
   90     "Data handler for GPass 0.4.x data"
   91 
   92     name        = "GPass 0.4.x"
   93     importer    = True
   94     exporter    = True
   95     encryption  = True
   96 
   97 
   98     def __init__(self):
   99         base.DataHandler.__init__(self)
  100 
  101 
  102     def export_data(self, entrystore, password):
  103         "Exports data to a data stream"
  104 
  105         data = "GNOME Password Manager\n"
  106 
  107         iter = entrystore.iter_nth_child(None, 0)
  108 
  109         while iter is not None:
  110             e = entrystore.get_entry(iter)
  111 
  112             if type(e) != entry.FolderEntry:
  113                 e = e.convert_generic()
  114 
  115                 data += e.name + "\n"
  116                 data += (e[entry.UsernameField] or "") + "\n"
  117                 data += (e[entry.PasswordField] or "") + "\n"
  118                 data += (e[entry.HostnameField] or "") + "\n"
  119                 data += str(e.updated) + "\n"
  120                 data += str(e.updated) + "\n"
  121                 data += "0\n"
  122                 data += str(len(e.description) + 1) + "\n"
  123                 data += e.description + "\n"
  124 
  125             iter = entrystore.iter_traverse_next(iter)
  126 
  127         return encrypt(data.encode(), password.encode())
  128 
  129 
  130     def import_data(self, input, password):
  131         "Imports data from a data stream to an entrystore"
  132 
  133         plaintext = decrypt(input, password, "GNOME Password Manager\n")
  134 
  135         entrystore = data.EntryStore()
  136         lines = plaintext.splitlines()
  137 
  138         while len(lines) > 0:
  139 
  140             e = entry.GenericEntry()
  141 
  142             e.name          = lines[0]
  143             e[entry.UsernameField]  = lines[1]
  144             e[entry.PasswordField]  = lines[2]
  145             e[entry.HostnameField]  = lines[3]
  146             e.updated       = int(lines[5])
  147             desclen         = int(lines[7])
  148 
  149             if e[entry.HostnameField] == "http://":
  150                 e[entry.HostnameField] = ""
  151 
  152             del lines[:8]
  153 
  154             d = ""
  155             while len(d) < desclen and len(lines) > 0:
  156                 d += lines[0] + "\n"
  157                 del lines[0]
  158 
  159             e.description = re.sub("[\r\n]+", " ", d).strip()
  160 
  161             entrystore.add_entry(e)
  162 
  163         return entrystore
  164 
  165 
  166 
  167 class GPass05(base.DataHandler):
  168     "Data handler for GPass 0.5.x data"
  169 
  170     name        = "GPass 0.5.x (or newer)"
  171     importer    = True
  172     exporter    = True
  173     encryption  = True
  174 
  175 
  176     def __init__(self):
  177         base.DataHandler.__init__(self)
  178 
  179 
  180     def __getint(self, input):
  181         "Fetches an integer from the input"
  182 
  183         if len(input) < 4:
  184             raise base.FormatError
  185 
  186         return ord(input[0]) << 0 | ord(input[1]) << 8 | ord(input[2]) << 16 | ord(input[3]) << 24
  187 
  188 
  189     def __getstr(self, input):
  190         "Fetches a string from the input"
  191 
  192         length = self.__getint(input[:4])
  193 
  194         if len(input) < (4 + length):
  195             raise base.FormatError
  196 
  197         string = input[4:4 + length]
  198 
  199         if len(string) != length:
  200             raise base.FormatError
  201 
  202         return string
  203 
  204 
  205     def __mkint(self, input):
  206         "Creates a string-representation of an integer"
  207 
  208         string = ""
  209 
  210         for i in range(4):
  211             string += chr(input >> i * 8 & 0xff)
  212 
  213         return string
  214 
  215 
  216     def __mkstr(self, input):
  217         "Makes a string suitable for inclusion in the data stream"
  218 
  219         return self.__mkint(len(input)) + input
  220 
  221 
  222     def __normstr(self, string):
  223         "Normalizes a string"
  224 
  225         string = re.sub("[\r\n]+", " ", string)
  226 
  227         return string
  228 
  229 
  230     def __packint(self, input):
  231         "Packs an integer"
  232 
  233         if input == 0:
  234             return "\x00"
  235 
  236         string = ""
  237 
  238         while input > 0:
  239             c   = input % 0x80
  240             input   = input // 0x80
  241 
  242             if input > 0:
  243                 c |= 0x80
  244 
  245             string += chr(c)
  246 
  247         return string
  248 
  249 
  250     def __packstr(self, input):
  251         "Packs a string"
  252 
  253         return self.__packint(len(input)) + input
  254 
  255 
  256     def __unpackint(self, input):
  257         "Fetches a packed number from the input"
  258 
  259         value   = 0
  260         b   = 1
  261 
  262         for i in range(min(len(input), 6)):
  263             c = ord(input[i])
  264 
  265             if c & 0x80:
  266                 value   += b * (c & 0x7f)
  267                 b   *= 0x80;
  268 
  269             else:
  270                 value   += b * c
  271 
  272                 return i + 1, value
  273 
  274         # if we didn't return in the for-loop, the input is invalid
  275         else:
  276             raise base.FormatError
  277 
  278 
  279     def __unpackstr(self, input):
  280         "Unpacks a string from the input"
  281 
  282         cut, length = self.__unpackint(input[:6])
  283 
  284         if len(input) < cut + length:
  285             raise base.FormatError
  286 
  287         return cut + length, input[cut:cut + length]
  288 
  289 
  290     def export_data(self, entrystore, password):
  291         "Exports data from an entrystore"
  292 
  293         plaintext   = "GPassFile version 1.1.0"
  294         iter        = entrystore.iter_children(None)
  295         id      = 0
  296         foldermap   = {}
  297 
  298         while iter != None:
  299             id += 1
  300 
  301             path        = entrystore.get_path(iter).to_string()
  302             parentpath  = ':'.join(path.split(':')[:-1])
  303 
  304             if len(parentpath) > 0 and parentpath in foldermap:
  305                 parentid = foldermap[parentpath]
  306 
  307             else:
  308                 parentid = 0
  309 
  310 
  311             e = entrystore.get_entry(iter)
  312 
  313             if type(e) == entry.FolderEntry:
  314                 foldermap[path] = id
  315 
  316             elif type(e) != entry.GenericEntry:
  317                 e = e.convert_generic()
  318 
  319 
  320             entrydata   = ""
  321             entrydata   += self.__mkint(id)
  322             entrydata   += self.__mkint(parentid)
  323             entrydata   += self.__mkstr(type(e) == entry.FolderEntry and "folder" or "general")
  324 
  325             attrdata    = ""
  326             attrdata    += self.__packstr(e.name)
  327             attrdata    += self.__packstr(e.description)
  328             attrdata    += self.__packint(e.updated)
  329             attrdata    += self.__packint(e.updated)
  330             attrdata    += self.__packint(0)
  331             attrdata    += self.__packint(0)
  332 
  333             if type(e) == entry.GenericEntry:
  334                 attrdata    += self.__packstr(e[entry.UsernameField] or "")
  335                 attrdata    += self.__packstr(e[entry.PasswordField] or "")
  336                 attrdata    += self.__packstr(e[entry.HostnameField] or "")
  337 
  338             entrydata   += self.__mkstr(attrdata)
  339             plaintext   += entrydata
  340 
  341             iter = entrystore.iter_traverse_next(iter)
  342 
  343         return encrypt(plaintext.encode(), password.encode())
  344 
  345 
  346     def import_data(self, input, password):
  347         "Imports data from a data stream to an entrystore"
  348 
  349         plaintext = decrypt(input, password, "GPassFile version 1.1.0")
  350 
  351         entrystore = data.EntryStore()
  352         foldermap = {}
  353 
  354         while len(plaintext) > 0:
  355 
  356             # parse data
  357             id      = self.__getint(plaintext[:4])
  358             plaintext   = plaintext[4:]
  359 
  360             parentid    = self.__getint(plaintext[:4])
  361             plaintext   = plaintext[4:]
  362 
  363             entrytype   = self.__getstr(plaintext)
  364             plaintext   = plaintext[4 + len(entrytype):]
  365 
  366             attrdata    = self.__getstr(plaintext)
  367             plaintext   = plaintext[4 + len(attrdata):]
  368 
  369 
  370             l, name     = self.__unpackstr(attrdata)
  371             attrdata    = attrdata[l:]
  372 
  373             l, desc     = self.__unpackstr(attrdata)
  374             attrdata    = attrdata[l:]
  375 
  376             l, ctime    = self.__unpackint(attrdata)
  377             attrdata    = attrdata[l:]
  378 
  379             l, mtime    = self.__unpackint(attrdata)
  380             attrdata    = attrdata[l:]
  381 
  382             l, expire   = self.__unpackint(attrdata)
  383             attrdata    = attrdata[l:]
  384 
  385             l, etime    = self.__unpackint(attrdata)
  386             attrdata    = attrdata[l:]
  387 
  388             if entrytype == "general":
  389                 l, username = self.__unpackstr(attrdata)
  390                 attrdata    = attrdata[l:]
  391 
  392                 l, password = self.__unpackstr(attrdata)
  393                 attrdata    = attrdata[l:]
  394 
  395                 l, hostname = self.__unpackstr(attrdata)
  396                 attrdata    = attrdata[l:]
  397 
  398             else:
  399                 username = password = hostname = ""
  400 
  401 
  402             # create entry
  403             if entrytype == "general":
  404                 e = entry.GenericEntry()
  405 
  406                 e.name          = self.__normstr(name)
  407                 e.description       = self.__normstr(desc)
  408                 e.updated       = mtime
  409 
  410                 e[entry.HostnameField]  = self.__normstr(hostname)
  411                 e[entry.UsernameField]  = self.__normstr(username)
  412                 e[entry.PasswordField]  = self.__normstr(password)
  413 
  414             elif entrytype == "folder":
  415                 e = entry.FolderEntry()
  416 
  417                 e.name          = self.__normstr(name)
  418                 e.description       = self.__normstr(desc)
  419                 e.updated       = mtime
  420 
  421             else:
  422                 continue
  423 
  424 
  425             # add entry to entrystore
  426             if parentid in foldermap:
  427                 parent = foldermap[parentid]
  428 
  429             else:
  430                 parent = None
  431 
  432             iter = entrystore.add_entry(e, parent)
  433 
  434             if type(e) == entry.FolderEntry:
  435                 foldermap[id] = iter
  436 
  437 
  438         return entrystore
  439