import struct
import sys
import time
+import types
try:
import enum34
except ImportError as exn:
, "NaCl_LEN" : 16 })
, "enc": "aes-gcm" } }
-
###############################################################################
## constants
###############################################################################
return SCRYPT_KEY_MEMO [key_parms], nacl
-def kdf_by_version (paramversion):
- defs = ENCRYPTION_PARAMETERS.get(paramversion)
+def kdf_by_version (paramversion=None, defs=None):
+ if paramversion is not None:
+ defs = ENCRYPTION_PARAMETERS.get(paramversion, None)
if defs is None:
raise ValueError ("no encryption parameters for version %r"
% paramversion)
## freestanding invocation
###############################################################################
+PDTCRYPT_SUB_PROCESS = 0
+PDTCRYPT_SUB_SCRYPT = 1
+
+PDTCRYPT_SUB = \
+ { "process" : PDTCRYPT_SUB_PROCESS
+ , "scrypt" : PDTCRYPT_SUB_SCRYPT }
+
PDTCRYPT_DECRYPT = 1 << 0 # decrypt archive with password
PDTCRYPT_SPLIT = 1 << 1 # split archive into individual objects
+PDTCRYPT_HASH = 1 << 2 # output scrypt hash for file and given password
PDTCRYPT_SPLITNAME = "pdtcrypt-object-%d.bin"
return depdtcrypt (pw, ins, outs)
+def mode_depdtcrypt (mode, pw, ins, outs):
+ try:
+ total_read, total_obj, total_ct, total_pt = \
+ depdtcrypt (mode, pw, ins, outs)
+ except DecryptionError as exn:
+ noise ("PDT: Decryption failed:")
+ noise ("PDT:")
+ noise ("PDT: “%s”" % exn)
+ noise ("PDT:")
+ noise ("PDT: Did you specify the correct password?")
+ noise ("")
+ return 1
+ except PDTSplitError as exn:
+ noise ("PDT: Split operation failed:")
+ noise ("PDT:")
+ noise ("PDT: “%s”" % exn)
+ noise ("PDT:")
+ noise ("PDT: Hint: target directory should to be empty.")
+ noise ("")
+ return 1
+
+ if PDTCRYPT_VERBOSE is True:
+ noise ("PDT: decryption successful" )
+ noise ("PDT: %.10d bytes read" % total_read)
+ noise ("PDT: %.10d objects decrypted" % total_obj )
+ noise ("PDT: %.10d bytes ciphertext" % total_ct )
+ noise ("PDT: %.10d bytes plaintext" % total_pt )
+ noise ("" )
+
+ return 0
+
+
+def mode_scrypt (pw, ins):
+ hdr = None
+ try:
+ hdr = hdr_read_stream (ins)
+ except EndOfFile as exn:
+ noise ("PDT: malformed input: end of file reading first object header")
+ noise ("PDT:")
+ return 1
+ finally:
+ ins.close ()
+
+ nacl = hdr ["nacl"]
+ pver = hdr ["paramversion"]
+ if PDTCRYPT_VERBOSE is True:
+ noise ("PDT: salt of first object : %s" % binascii.hexlify (nacl))
+ noise ("PDT: parameter version of archive : %d" % pver)
+
+ try:
+ defs = ENCRYPTION_PARAMETERS.get(pver, None)
+ kdfname, params = defs ["kdf"]
+ if kdfname != "scrypt":
+ noise ("PDT: input is not an SCRYPT archive")
+ noise ("")
+ return 1
+ kdf = kdf_by_version (None, defs)
+ except ValueError as exn:
+ noise ("PDT: object has unknown parameter version %d" % pver)
+
+ hsh, _void = kdf (pw, nacl)
+
+ import json
+ out = json.dumps ({ "salt" : str (binascii.hexlify (nacl))
+ , "hash" : str (binascii.hexlify (hsh))
+ , "scrypt_params" : { "N" : params ["N"]
+ , "r" : params ["r"]
+ , "p" : params ["p"]
+ , "dkLen" : params ["dkLen"] } })
+ print (out)
+
+
def usage (err=False):
out = print
if err is True:
out = noise
- out ("usage: %s { --help" % SELF)
- out (" | [ -v ] { PASSWORD } { { -i | --in } { - | SOURCE } }")
- out (" { { -o | --out } { - | DESTINATION } }")
- out (" { -D | --no-decrypt } { -S | --split }")
+ out ("usage: %s SUBCOMMAND { --help" % SELF)
+ out (" | [ -v ] { PASSWORD } { { -i | --in } { - | SOURCE } }")
+ out (" { { -o | --out } { - | DESTINATION } }")
+ out (" { -D | --no-decrypt } { -S | --split }")
out ("")
out ("\twhere")
+ out ("\t\tSUBCOMMAND main mode: { process | scrypt }")
+ out ("\t\t where:")
+ out ("\t\t process: extract objects from PDT archive")
+ out ("\t\t scrypt: calculate hash from password and first object")
out ("\t\tPASSWORD password to derive the encryption key from")
out ("\t\t-s enforce strict handling of initialization vectors")
out ("\t\t-i SOURCE file name to read from")
argvi = iter (argv)
SELF = os.path.basename (next (argvi))
+ try:
+ rawsubcmd = next (argvi)
+ subcommand = PDTCRYPT_SUB [rawsubcmd]
+ except StopIteration:
+ noise ("ERROR: subcommand required")
+ noise ("")
+ usage (err=True)
+ raise Unreachable
+ except KeyError:
+ noise ("ERROR: invalid subcommand “%s” specified" % rawsubcmd)
+ noise ("")
+ usage (err=True)
+ raise Unreachable
+
+ def checked_pw (arg):
+ nonlocal pw
+ if pw is None:
+ pw = arg
+ else:
+ noise ("ERROR: unqualified argument “%s” but password already "
+ "given" % arg)
+ noise ("")
+ usage (err=True)
+ raise Unreachable
+
for arg in argvi:
if arg in [ "-h", "--help" ]:
usage ()
elif arg in [ "-v", "--verbose", "--wtf" ]:
global PDTCRYPT_VERBOSE
PDTCRYPT_VERBOSE = True
- elif arg in [ "-s", "--strict-ivs" ]:
- global PDTCRYPT_STRICTIVS
- PDTCRYPT_STRICTIVS = True
elif arg in [ "-i", "--in", "--source" ]:
insspec = next (argvi)
if PDTCRYPT_VERBOSE is True: noise ("PDT: decrypt from %s" % insspec)
elif arg in [ "-o", "--out", "--dest", "--sink" ]:
outsspec = next (argvi)
if PDTCRYPT_VERBOSE is True: noise ("PDT: decrypt to %s" % outsspec)
- elif arg in [ "-f", "--force" ]:
- global PDTCRYPT_OVERWRITE
- PDTCRYPT_OVERWRITE = True
- if PDTCRYPT_VERBOSE is True: noise ("PDT: overwrite existing files")
- elif arg in [ "-S", "--split" ]:
- mode |= PDTCRYPT_SPLIT
- if PDTCRYPT_VERBOSE is True: noise ("PDT: split files")
- elif arg in [ "-D", "--no-decrypt" ]:
- mode &= ~PDTCRYPT_DECRYPT
- if PDTCRYPT_VERBOSE is True: noise ("PDT: not decrypting")
else:
- if pw is None:
- pw = arg
+ if subcommand == PDTCRYPT_SUB_PROCESS:
+ if arg in [ "-s", "--strict-ivs" ]:
+ global PDTCRYPT_STRICTIVS
+ PDTCRYPT_STRICTIVS = True
+ elif arg in [ "-f", "--force" ]:
+ global PDTCRYPT_OVERWRITE
+ PDTCRYPT_OVERWRITE = True
+ if PDTCRYPT_VERBOSE is True: noise ("PDT: overwrite existing files")
+ elif arg in [ "-S", "--split" ]:
+ mode |= PDTCRYPT_SPLIT
+ if PDTCRYPT_VERBOSE is True: noise ("PDT: split files")
+ elif arg in [ "-D", "--no-decrypt" ]:
+ mode &= ~PDTCRYPT_DECRYPT
+ if PDTCRYPT_VERBOSE is True: noise ("PDT: not decrypting")
+ else:
+ checked_pw (arg)
+ elif subcommand == PDTCRYPT_SUB_SCRYPT:
+ checked_pw (arg)
else:
- noise ("ERROR: unqualified argument “%s” but password already "
- "given" % arg)
- noise ("")
- usage (err=True)
raise Unreachable
- if mode & PDTCRYPT_DECRYPT and pw is None:
- noise ("ERROR: encryption requested but no password given")
- noise ("")
- usage (err=True)
- raise Unreachable
+ if pw is not None:
+ pw = pw.encode ()
+ else:
+ if subcommand == PDTCRYPT_SUB_SCRYPT:
+ noise ("ERROR: scrypt hash mode requested but no password given")
+ noise ("")
+ usage (err=True)
+ raise Unreachable
+ elif mode & PDTCRYPT_DECRYPT:
+ noise ("ERROR: encryption requested but no password given")
+ noise ("")
+ usage (err=True)
+ raise Unreachable
# default to stdout
ins = deptdcrypt_mk_stream (PDTCRYPT_SOURCE, insspec or "-")
+
+ if subcommand == PDTCRYPT_SUB_SCRYPT:
+ return True, partial (mode_scrypt, pw, ins)
+
if mode & PDTCRYPT_SPLIT: # destination must be directory
if outsspec is None or outsspec == "-":
noise ("ERROR: split mode is incompatible with stdout sink")
noise ("")
usage (err=True)
raise Unreachable
+
else:
outs = deptdcrypt_mk_stream (PDTCRYPT_SINK, outsspec or "-")
- return mode, pw, ins, outs
+
+ return True, partial (mode_depdtcrypt, mode, pw, ins, outs)
def main (argv):
- mode, pw, ins, outs = parse_argv (argv)
- try:
- total_read, total_obj, total_ct, total_pt = \
- depdtcrypt (mode, pw, ins, outs)
- except DecryptionError as exn:
- noise ("PDT: Decryption failed:")
- noise ("PDT:")
- noise ("PDT: “%s”" % exn)
- noise ("PDT:")
- noise ("PDT: Did you specify the correct password?")
- noise ("")
- return 1
- except PDTSplitError as exn:
- noise ("PDT: Split operation failed:")
- noise ("PDT:")
- noise ("PDT: “%s”" % exn)
- noise ("PDT:")
- noise ("PDT: Hint: target directory should to be empty.")
- noise ("")
- return 1
+ ok, runner = parse_argv (argv)
- if PDTCRYPT_VERBOSE is True:
- noise ("PDT: decryption successful" )
- noise ("PDT: %.10d bytes read" % total_read)
- noise ("PDT: %.10d objects decrypted" % total_obj )
- noise ("PDT: %.10d bytes ciphertext" % total_ct )
- noise ("PDT: %.10d bytes plaintext" % total_pt )
- noise ("" )
+ if ok is True: return runner ()
- return 0
+ return 1
if __name__ == "__main__":