"16s") # GCM tag
# aes+gcm
+AES_KEY_SIZE = 16 # b"0123456789abcdef"
+AES_KEY_SIZE_B64 = 24 # b'MDEyMzQ1Njc4OWFiY2RlZg=='
AES_GCM_MAX_SIZE = (1 << 36) - (1 << 5) # 2^39 - 2^8 b ≅ 64 GB
PDTCRYPT_MAX_OBJ_SIZE_DEFAULT = 63 * (1 << 30) # 63 GB
PDTCRYPT_MAX_OBJ_SIZE = PDTCRYPT_MAX_OBJ_SIZE_DEFAULT
PDTCRYPT_IV_FIXEDPART_SIZE = 8 # B
PDTCRYPT_IV_COUNTER_SIZE = 4 # B
+# secret type: PW of string | KEY of char [16]
+PDTCRYPT_SECRET_PW = 0
+PDTCRYPT_SECRET_KEY = 1
+
###############################################################################
## header, trailer
###############################################################################
###############################################################################
+## helpers
+###############################################################################
+
+def make_secret (password=None, key=None):
+ """
+ Safely create a “secret” value that consists either of a key or a password.
+ Inputs are validated: the password is accepted as (UTF-8 encoded) bytes or
+ string; for the key only a bytes object of the proper size or a base64
+ encoded string thereof is accepted.
+
+ If both are provided, the key is preferred over the password; no checks are
+ performed whether the key is derived from the password.
+
+ :returns: secret value if inputs were acceptable | None otherwise.
+ """
+ if key is not None:
+ if isinstance (key, str) is True:
+ key = key.encode ("utf-8")
+ if isinstance (key, bytes) is True:
+ if len (key) == AES_KEY_SIZE:
+ return (PDTCRYPT_SECRET_KEY, key)
+ if len (key) == AES_KEY_SIZE_B64:
+ try:
+ key = base64.b64decode (key)
+ # the base64 processor is very tolerant and allows for
+ # arbitrary traling and leading data thus the data obtained
+ # must be checked for the proper length
+ if len (key) == AES_KEY_SIZE:
+ return (PDTCRYPT_SECRET_KEY, key)
+ except binascii.Error: # “incorrect padding”
+ pass
+ elif password is not None:
+ if isinstance (password, str) is True:
+ return (PDTCRYPT_SECRET_PW, password)
+ elif isinstance (password, bytes) is True:
+ try:
+ password = password.decode ("utf-8")
+ return (PDTCRYPT_SECRET_PW, password)
+ except UnicodeDecodeError:
+ pass
+
+ return None
+
+
+###############################################################################
## passthrough / null encryption
###############################################################################
, "scrypt" : PDTCRYPT_SUB_SCRYPT
, "scan" : PDTCRYPT_SUB_SCAN }
-PDTCRYPT_SECRET_PW = 0
-PDTCRYPT_SECRET_KEY = 1
-
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
except StopIteration:
bail ("ERROR: argument list incomplete")
- def checked_secret (t, arg):
+ def checked_secret (s):
nonlocal secret
if secret is None:
- secret = (t, arg)
+ secret = s
else:
bail ("ERROR: encountered “%s” but secret already given" % arg)
if PDTCRYPT_VERBOSE is True: noise ("PDT: decrypt from %s" % insspec)
elif arg in [ "-p", "--password" ]:
arg = checked_arg ()
- checked_secret (PDTCRYPT_SECRET_PW, arg)
+ checked_secret (make_secret (password=arg))
if PDTCRYPT_VERBOSE is True: noise ("PDT: decrypting with password")
else:
if subcommand == PDTCRYPT_SUB_PROCESS:
if PDTCRYPT_VERBOSE is True: noise ("PDT: not decrypting")
elif arg in [ "-k", "--key" ]:
arg = checked_arg ()
- checked_secret (PDTCRYPT_SECRET_KEY, arg)
+ checked_secret (make_secret (key=arg))
if PDTCRYPT_VERBOSE is True: noise ("PDT: decrypting with key")
else:
bail ("ERROR: unexpected positional argument “%s”" % arg)
noise ("ERROR: no password or key specified, trying $PDTCRYPT_PASSWORD")
epw = os.getenv ("PDTCRYPT_PASSWORD")
if epw is not None:
- checked_secret (PDTCRYPT_SECRET_PW, epw.strip ())
+ checked_secret (make_secret (password=epw.strip ()))
if secret is None:
if PDTCRYPT_VERBOSE is True:
noise ("ERROR: no password or key specified, trying $PDTCRYPT_KEY")
ek = os.getenv ("PDTCRYPT_KEY")
if ek is not None:
- checked_secret (PDTCRYPT_SECRET_KEY, ek.strip ())
+ checked_secret (make_secret (key=ek.strip ()))
if secret is None:
if subcommand == PDTCRYPT_SUB_SCRYPT: