chiark / gitweb /
catacomb/pwsafe.py: Eliminate the Buffer class and struct module.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 24 May 2014 13:00:03 +0000 (14:00 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Thu, 14 Aug 2014 18:57:23 +0000 (19:57 +0100)
All is done using Catacomb's ReadBuffer and WriteBuffer classes.

catacomb/pwsafe.py

index d0d1a35b027e131366fe75c34d536e79de5f46b8..3af69d4c8cd90dc8e0db468e58d6f44938d77477 100644 (file)
 
 import catacomb as _C
 import gdbm as _G
-import struct as _S
-
-###--------------------------------------------------------------------------
-### Utilities.
-
-class Buffer (object):
-  """
-  I am a simple gadget for parsing binary strings.
-
-  You should use Catacomb's ReadBuffer instead.
-  """
-
-  def __init__(me, s):
-    """
-    Initialize the buffer with a string S.
-    """
-    me.str = s
-    me.i = 0
-
-  def get(me, n):
-    """
-    Fetch and return the next N bytes from the buffer.
-    """
-    i = me.i
-    if n + i > len(me.str):
-      raise IndexError, 'buffer underflow'
-    me.i += n
-    return me.str[i:i + n]
-
-  def getbyte(me):
-    """
-    Fetch and return (as a small integer) the next byte from the buffer.
-    """
-    return ord(me.get(1))
-
-  def unpack(me, fmt):
-    """
-    Unpack a structure described by FMT from the next bytes of the buffer.
-
-    Return a tuple containing the unpacked items.
-    """
-    return _S.unpack(fmt, me.get(_S.calcsize(fmt)))
-
-  def getstring(me):
-    """
-    Fetch and return a counted string from the buffer.
-
-    The string is expected to be preceded by its 16-bit length, in network
-    byte order.
-    """
-    return me.get(me.unpack('>H')[0])
-
-  def checkend(me):
-    """
-    Raise an error if the buffer has not been completely consumed.
-    """
-    if me.i != len(me.str):
-      raise ValueError, 'junk at end of buffer'
-
-def _wrapstr(s):
-  """
-  Prefix the string S with its 16-bit length.
-
-  It can be read using Buffer.getstring.  You should use Catacomb's
-  WriteBuffer.putblk16() function instead.
-  """
-  return _S.pack('>H', len(s)) + s
 
 ###--------------------------------------------------------------------------
 ### Underlying cryptography.
@@ -137,27 +70,35 @@ class Crypto (object):
     """
     Encrypt the message PT and return the resulting ciphertext.
     """
-    if me.c.__class__.blksz:
-      iv = _C.rand.block(me.c.__class__.blksz)
+    blksz = me.c.__class__.blksz
+    b = _C.WriteBuffer()
+    if blksz:
+      iv = _C.rand.block(blksz)
       me.c.setiv(iv)
-    else:
-      iv = ''
-    y = iv + me.c.encrypt(pt)
-    t = me.m().hash(y).done()
-    return t + y
+      b.put(iv)
+    b.put(me.c.encrypt(pt))
+    t = me.m().hash(b).done()
+    return t + str(buffer(b))
+
   def decrypt(me, ct):
     """
     Decrypt the ciphertext CT, returning the plaintext.
 
     Raises DecryptError if anything goes wrong.
     """
-    t = ct[:me.m.__class__.tagsz]
-    y = ct[me.m.__class__.tagsz:]
-    if t != me.m().hash(y).done():
-      raise DecryptError
-    iv = y[:me.c.__class__.blksz]
-    if me.c.__class__.blksz: me.c.setiv(iv)
-    return me.c.decrypt(y[me.c.__class__.blksz:])
+    blksz = me.c.__class__.blksz
+    tagsz = me.m.__class__.tagsz
+    b = _C.ReadBuffer(ct)
+    t = b.get(tagsz)
+    h = me.m()
+    if blksz:
+      iv = b.get(blksz)
+      me.c.setiv(iv)
+      h.hash(iv)
+    x = b.get(b.left)
+    h.hash(x)
+    if t != h.done(): raise DecryptError
+    return me.c.decrypt(x)
 
 class PPK (Crypto):
   """
@@ -271,13 +212,13 @@ class PW (object):
     tag = me.db['tag']
     ppk = PPK(_C.ppread(tag), c, h, m, me.db['salt'])
     try:
-      buf = Buffer(ppk.decrypt(me.db['key']))
+      b = _C.ReadBuffer(ppk.decrypt(me.db['key']))
     except DecryptError:
       _C.ppcancel(tag)
       raise
-    me.ck = buf.getstring()
-    me.mk = buf.getstring()
-    buf.checkend()
+    me.ck = b.getblk16()
+    me.mk = b.getblk16()
+    if not b.endp: raise ValueError, 'trailing junk'
 
     ## Set the key, and stash it and the tag-hashing secret.
     me.k = Crypto(c, h, m, me.ck, me.mk)
@@ -309,7 +250,7 @@ class PW (object):
     db['cipher'] = c.name
     db['hash'] = h.name
     db['mac'] = m.name
-    db['key'] = ppk.encrypt(_wrapstr(ck) + _wrapstr(mk))
+    db['key'] = ppk.encrypt(_C.WriteBuffer().putblk16(ck).putblk16(mk))
     db['magic'] = k.encrypt(_C.rand.block(h.hashsz))
 
   def keyxform(me, key):
@@ -329,17 +270,18 @@ class PW (object):
     _C.ppcancel(tag)
     ppk = PPK(_C.ppread(tag, _C.PMODE_VERIFY),
               me.k.c.__class__, me.k.h, me.k.m.__class__)
-    me.db['key'] = ppk.encrypt(_wrapstr(me.ck) + _wrapstr(me.mk))
+    me.db['key'] = \
+        ppk.encrypt(_C.WriteBuffer().putblk16(me.ck).putblk16(me.mk))
     me.db['salt'] = ppk.salt
 
   def pack(me, key, value):
     """
     Pack the KEY and VALUE into a ciphertext, and return it.
     """
-    w = _wrapstr(key) + _wrapstr(value)
-    pl = (len(w) + 255) & ~255
-    w += '\0' * (pl - len(w))
-    return me.k.encrypt(w)
+    b = _C.WriteBuffer()
+    b.putblk16(key).putblk16(value)
+    b.zero(((b.size + 255) & ~255) - b.size)
+    return me.k.encrypt(b)
 
   def unpack(me, ct):
     """
@@ -347,9 +289,9 @@ class PW (object):
 
     Might raise DecryptError, of course.
     """
-    buf = Buffer(me.k.decrypt(ct))
-    key = buf.getstring()
-    value = buf.getstring()
+    b = _C.ReadBuffer(me.k.decrypt(ct))
+    key = b.getblk16()
+    value = b.getblk16()
     return key, value
 
   ## Mapping protocol.