return HDR_CAND_GOOD, hdr
-def try_decrypt (fd, off, hdr, fname=None):
+def try_decrypt (fd, off, hdr, secret, fname=None):
"""
Attempt to decrypt the object in the (seekable) descriptor *fd* starting at
- *off* using the metadata in *hdr*. An output file can be specified with
- *fname*; if it is *None*, the decrypted payload will be discarded.
+ *off* using the metadata in *hdr* and *secret*. An output file can be
+ specified with *fname*; if it is *None*, the decrypted payload will be
+ discarded.
+
+ Always creates a fresh decryptor, so validation steps across objects don’t
+ apply.
"""
+ ctleft = hdr ["ctsize"]
+ pos = off
+
+ ks = secret [0]
+ if ks == PDTCRYPT_SECRET_PW:
+ decr = Decrypt (password=secret [1])
+ elif ks == PDTCRYPT_SECRET_KEY:
+ key = binascii.unhexlify (secret [1])
+ decr = Decrypt (key=key)
+ else:
+ raise RuntimeError
+
+ if fname is not None: raise NotImplementedError
+
+ decr.next (hdr)
+
+ try:
+ os.lseek (fd, pos, os.SEEK_SET)
+ while ctleft > 0:
+ cnksiz = min (ctleft, PDTCRYPT_BLOCKSIZE)
+ cnk = os.read (fd, cnksiz)
+ ctleft -= cnksiz
+ pos += cnksiz
+ _pt = decr.process (cnk)
+
+ _pt = decr.done ()
+ except Exception as exn:
+ noise ("PDT: error decrypting object %d–%d@%d, %d B remaining [%s]"
+ % (off, off + hdr ["ctsize"], pos, ctleft, exn))
+ raise
- raise NotImplementedError
+ return pos - off
###############################################################################
noise (line)
-def mode_scan (pw, fname, nacl=None):
+def mode_scan (secret, fname, nacl=None):
"""
Dissect a binary file, looking for PDTCRYPT headers and objects.
"""
else:
off0 = cand + PDTCRYPT_HDR_SIZE
if PDTCRYPT_VERBOSE is True:
- noise ("PDT: read payload @%d: %s" % (off0, hdr_fmt (hdr)))
- vdtt = try_decrypt (fd, off0, hdr)
+ noise ("PDT: read payload @%d" % off0)
+ pretty = hdr_fmt_pretty (hdr)
+ noise (reduce (lambda a, e: (a + "\n" if a else "") + "PDT:\t· " + e,
+ pretty.splitlines (), ""))
- if vdt == HDR_CAND_GOOD and vdtt == HDR_CAND_GOOD:
+ ok = try_decrypt (fd, off0, hdr, secret) == hdr ["ctsize"]
+ if vdt == HDR_CAND_GOOD and ok is True:
noise ("PDT: %d → ✓ valid object %d–%d"
% (cand, off0, off0 + hdr ["ctsize"]))
- elif vdt == HDR_CAND_FISHY and vdtt == HDR_CAND_GOOD:
+ elif vdt == HDR_CAND_FISHY and ok is True:
noise ("PDT: %d → × object %d–%d, corrupt header"
% (cand, off0, off0 + hdr ["ctsize"]))
- elif vdt == HDR_CAND_GOOD and vdtt == HDR_CAND_FISHY:
+ elif vdt == HDR_CAND_GOOD and ok is False:
noise ("PDT: %d → × object %d–%d, problematic payload"
% (cand, off0, off0 + hdr ["ctsize"]))
- elif vdt == HDR_CAND_FISHY and vdtt == HDR_CAND_FISHY:
+ elif vdt == HDR_CAND_FISHY and ok is False:
noise ("PDT: %d → × object %d–%d, corrupt header, problematic "
"ciphertext" % (cand, off0, off0 + hdr ["ctsize"]))
else:
finally:
os.close (fd)
+ if len (junk) == 0:
+ noise ("PDT: all headers ok")
+ else:
+ noise ("PDT: %d candidates not parseable as headers:" % len (junk))
+ noise_output_candidates (junk)
+
def usage (err=False):
out = print
if err is True:
bail ("ERROR: please supply an input file for scanning")
if insspec == '-':
bail ("ERROR: input must be seekable; please specify a file")
- return True, partial (mode_scan, secret [1].encode (), insspec, nacl)
+ return True, partial (mode_scan, secret, insspec, nacl)
if subcommand == PDTCRYPT_SUB_SCRYPT:
if secret [0] == PDTCRYPT_SECRET_KEY: