Skip to main content

Dead-simple password-based file encryption for the command line and Python (scrypt + Fernet).

Project description

pydlock

pydlock

DOI

Description

pydlock is a dead-simple tool for password-encrypting and decrypting files. Lock a file with one command, unlock it with another — that is the whole product. It can be used from the command line or imported as a Python package.

As of 2.0 your password is protected with a salted, memory-hard scrypt key derivation, files of any kind (including binaries and Windows executables) round-trip losslessly, and writes are atomic — an interrupted lock or unlock never leaves a truncated or half-written file (see Security boundaries for the precise durability and integrity guarantees).

Problems this solves

Reach for pydlock if you are trying to:

  • Password-encrypt a file from the command line — one command to lock, one to unlock, nothing else to configure.
  • Encrypt and decrypt a file in Python with a two-function API (pydlock.lock / pydlock.unlock) instead of wiring up a crypto library yourself.
  • Protect a file with a strong password-derived key without designing your own scheme — pydlock uses salted, memory-hard scrypt and authenticated Fernet (AES-128-CBC + HMAC-SHA256), and adds no custom cryptography.
  • Encrypt binaries safely — files of any kind round-trip byte-for-byte, and writes are atomic (a fresh file is prepared and swapped into place, so an interrupted operation never truncates your data).

Installation

pydlock is available on the Python Package Index (PyPI) at https://pypi.org/project/pydlock. Install it with pip:

pip install pydlock

Quick start

Encrypt a file in place:

pydlock lock secret.txt

Decrypt it again:

pydlock unlock secret.txt

That is the entire everyday workflow. You are prompted for a password (twice when locking); nothing else is required.

Usage

From the command line

The pydlock console command (installed with the package) and python -m pydlock are equivalent:

user@computer:~$ pydlock -h
usage: pydlock [-h] [--version] [--encoding ENCODING] {lock,unlock,encrypt,decrypt} file

positional arguments:
    {lock,unlock,encrypt,decrypt}
    file

options:
    -h, --help           show this help message and exit
    --version            show the version and exit
    --encoding ENCODING

Supported operations:

  • lock — encrypt a file in place.
  • unlock — decrypt a file in place.
  • encrypt — alias for lock.
  • decrypt — alias for unlock.

A short example:

user@computer:~$ cat secret.txt
Shh! It's a secret!

user@computer:~$ pydlock lock secret.txt
Enter password:
Re-enter password:

user@computer:~$ pydlock unlock secret.txt
Enter password:

user@computer:~$ cat secret.txt
Shh! It's a secret!

An entered-but-wrong password fails cleanly — pydlock prints Could not decrypt (wrong password or corrupt file). and leaves the encrypted file untouched. Other expected problems (a missing file, a symlink or hard-linked target, a permission error) print a one-line pydlock: … diagnostic and exit non-zero, without a traceback.

In other Python modules

import pydlock

filename = "secret.txt"

with open(filename, "wb") as file:

    file.write(b"Shh! It's a secret!")

pydlock.lock(filename)      # prompts for a password, then encrypts in place
pydlock.unlock(filename)    # prompts for the password, then decrypts

What's new in 2.0

Version 2.0 is a breaking change to the on-disk format. Files are now written as a small self-identifying envelope — a PYDLOCK magic marker, a JSON header carrying the key-derivation parameters and a per-file random salt, and then the encrypted token — instead of a bare token.

Highlights:

  • Stronger password protection. The key is derived with a salted, memory-hard scrypt KDF (see below), replacing the previous unsalted single-pass SHA-256 derivation.
  • Binary files are safe. Files are read and written as raw bytes, so binary files and Windows executables round-trip losslessly. Earlier versions corrupted them; that bug is fixed.
  • Atomic writes. Locking and unlocking write to a temporary file and atomically replace the original, so an interrupted operation can never leave a truncated or half-written file. On POSIX the file and its parent directory are fsynced so the replacement is also durable across a power loss; see Security boundaries for the per-platform guarantee.
  • encrypt / decrypt aliases for lock / unlock.
  • python and run removed. The old decrypt-and-execute subcommands were a security footgun (arbitrary code execution) and outside the scope of a file-encryption tool; they have been removed.

Migrating from v1

You do not need to do anything special. Files locked with pydlock 1.x are detected automatically and decrypted transparently:

user@computer:~$ pydlock unlock old_v1_file.txt
Enter password:

Re-locking an unlocked file rewrites it in the new v2 format, so a file is upgraded simply by unlocking and locking it again.

If you ever need the old behavior explicitly, the final 1.x release remains installable as a documented fallback:

pip install 'pydlock<2'

How your password is protected

When you lock a file, pydlock generates a fresh 16-byte random salt and derives the encryption key from your password with scrypt (parameters n = 32768, r = 8, p = 1), a memory-hard function designed to make brute-force and hardware-accelerated guessing expensive. The salt and parameters are stored in the file's header so the key can be re-derived when you unlock it — a different salt each time means locking the same file twice never produces the same ciphertext.

The file itself is encrypted with Fernet (AES-128 in CBC mode with an HMAC-SHA256 authentication tag) from the well-vetted cryptography library. Because the token is authenticated, a wrong password, a corrupted token, or any modification of the encrypted contents is detected and rejected — pydlock never returns silently-wrong plaintext. pydlock adds no custom cryptography of its own.

Use a strong passphrase: scrypt makes guessing expensive, but it cannot add entropy to a weak or empty one. pydlock refuses to encrypt with an empty password.

Security boundaries

pydlock is deliberately small. Knowing exactly what it does and does not guarantee lets you use it safely.

  • What is authenticated. The plaintext and the ciphertext are authenticated by Fernet's HMAC, and the v2 envelope is parsed strictly (exact framing, canonical base64, the promised salt length), so a wrong password or a modified token/header is rejected rather than returning wrong plaintext. This is not a byte-for-byte signature over an arbitrary presentation of the file: Fernet has no additional-authenticated-data channel, so pydlock authenticates the plaintext and ciphertext and validates the envelope grammar, rather than claiming every possible byte layout is signed.
  • Regular files only; no aliases. lock and unlock operate only on an existing, singly-linked regular file. A symlink, a hard-linked file (st_nlink != 1), or a non-regular file (directory, device, FIFO) is rejected with a non-zero exit, because pydlock replaces a file by renaming a new inode over the path — which would encrypt one name while leaving the plaintext reachable through the other. Resolve the symlink yourself and pass the real target if that is what you meant.
  • Concurrent edits are not clobbered. pydlock snapshots the file's identity when it reads it and revalidates immediately before replacing it. If the file changed on disk in between (a concurrent writer, or a path swap), the operation aborts without overwriting the newer contents. A small time window between the check and the replace is unavoidable; pydlock is not a substitute for a file lock in a heavily concurrent workflow.
  • Atomic everywhere; durable on POSIX. The replace is atomic on every platform (no truncated/partial file). Full power-loss durability additionally requires fsyncing the parent directory, which pydlock does on POSIX; on Windows and on filesystems that do not support directory fsync, only atomic replacement is guaranteed, not durability across a sudden power loss.
  • Metadata Fernet exposes. A Fernet token embeds the creation timestamp in cleartext (it is authenticated, not hidden), so an observer can read when a file was locked. The ciphertext length also reveals the approximate plaintext size. pydlock does not pad or hide either.
  • Whole-file, in-memory. pydlock reads the entire file into memory and Fernet requires the whole message at once, so inputs must fit comfortably in RAM. Fernet is not intended for very large files; a streaming format is explicitly out of scope for pydlock.

Copyright and License

Pydlock - A Python file encryption tool.

Copyright (c) 2020 of Erick Edward Shepherd, all rights reserved.

Released under the MIT License. See the LICENSE file for the full text. Built by Erick Shepherd.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pydlock-2.0.7.tar.gz (39.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pydlock-2.0.7-py3-none-any.whl (21.0 kB view details)

Uploaded Python 3

File details

Details for the file pydlock-2.0.7.tar.gz.

File metadata

  • Download URL: pydlock-2.0.7.tar.gz
  • Upload date:
  • Size: 39.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pydlock-2.0.7.tar.gz
Algorithm Hash digest
SHA256 6e7e85dbac45c25edd2034b09a2de071d6f59704291d4ec09af2ff8e1ae3ae8f
MD5 df181894ca78cbcf77ce67687b40e7ba
BLAKE2b-256 17237d53f4bf3193767ae384c162d105bd624225ab9c5e3f15dfe14f420dcce6

See more details on using hashes here.

Provenance

The following attestation bundles were made for pydlock-2.0.7.tar.gz:

Publisher: publish.yml on ErickShepherd/pydlock

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pydlock-2.0.7-py3-none-any.whl.

File metadata

  • Download URL: pydlock-2.0.7-py3-none-any.whl
  • Upload date:
  • Size: 21.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pydlock-2.0.7-py3-none-any.whl
Algorithm Hash digest
SHA256 a03a4f73ada8fe6ff7298491f23e7c281f0789d514a1d1ebf68f339e36f5fbf0
MD5 8baa2cb3f11f9c41414d0d4f17c3b5df
BLAKE2b-256 ad97ef4d1962d84c339b50ecc9141cd9268eca9f5337464b02ec28035b02b25d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pydlock-2.0.7-py3-none-any.whl:

Publisher: publish.yml on ErickShepherd/pydlock

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page