Open
No open findings
Positive Security Controls
The following controls are correctly and robustly implemented — no action required
- Encrypt-then-MAC — HMAC-SHA256 is computed over the full header (version + iteration count + salt + IV) and the ciphertext, and verified before decryption, closing CBC padding-oracle attacks and authenticating the stored parameters.
- Constant-time MAC comparison —
ConstantTimeEquals uses bitwise XOR accumulation to eliminate timing side-channels on MAC verification.
- PBKDF2-SHA256 at 800,000 iterations — exceeds OWASP's recommended minimum for PBKDF2-HMAC-SHA256 (600,000 iterations, as at September 2026); provides strong key stretching against brute-force.
- Self-describing, versioned container — each encrypted output carries a one-byte format version and the PBKDF2 iteration count in its header (both authenticated by the HMAC). Because the count used is read back from each file rather than assumed, a future release can raise the default iteration count as hardware improves and still decrypt files produced by earlier versions — iteration strength can grow without breaking backward compatibility. The stored count is range-checked before it feeds PBKDF2, so a crafted file cannot force an oversized key-derivation run, and the version byte lets any later format change be detected rather than mis-parsed.
- Key separation — 64 bytes from PBKDF2 are split into distinct AES-256 and HMAC-SHA256 keys; no key reuse between operations.
- Random salt and IV per encryption —
Crypto.GenerateRandomBytes is used for both, preventing ciphertext replay and pre-computation.
- Derived key zeroing on all exit paths —
ZeroBlock is called on encKey, macKey, and kdf in the success path, MAC-failure path, and exception handler.
- SQL injection prevention — all database queries in
mSettings use SQLitePreparedStatement with typed bindings; no string-concatenated SQL anywhere.
- No network communication — the application makes no outbound connections; attack surface is limited entirely to local resources and user-supplied input.
- Passphrase cleared after operation —
edtSecret and edtSecretConfirm are cleared on all exit paths of both btnEncrypt and btnDecrypt — success and failure — and once a file drop completes, including the DropObject exception handler, so the passphrase does not persist in UI controls after a cryptographic operation.
- Sensitive fields cleared after inactivity — an idle timer restarted on any change to the secret, plaintext, or ciphertext fields (typing, paste, or a displayed result) wipes all three after five minutes of inactivity, and keeps running across focus changes so an unattended session clears itself even while the app is in the background. A visible on-screen countdown (
Idle Time Clear Down: ns) shows the seconds remaining while the timer runs and is hidden once the fields are cleared, so the pending wipe is never a surprise. Clearing is deliberately not tied to window deactivation: dragging a file from the Desktop to encrypt necessarily deactivates the app, and clearing on focus loss would destroy the secret before the drop lands.
- File payloads zeroed before release —
ZeroPayload overwrites the plaintext zip buffer on both file paths before it is released to the allocator, in chunked writes so a 1 GB block is cleared without a per-byte loop.
- Authentication precedes any output on the file path — a dropped
.ncryptaes file fails at the constant-time MAC check inside AESCryptData before the recovered archive is written or expanded, so a wrong secret or a tampered file produces no Desktop output and no temporary artefact at all.
- Unpredictable, private staging folder —
File_CreateWorkFolder names each operation's staging folder from Crypto.GenerateRandomBytes rather than the system clock, refuses a path that already exists instead of writing through it, and sets owner-only permissions on macOS and Linux. A local attacker on a shared temporary folder can no longer pre-place a file or symlink at a guessable path, and losing the race between the existence check and CreateFolder is safe because CreateFolder raises on an existing path and the operation is abandoned.
- Temporary artefacts overwritten and removed on every exit path — the staged plaintext zip is overwritten in place by
File_ShredTemporary before it is unlinked, and the work folder holding it and the expansion folder is removed on success, on each early-return failure, and in the exception handler of both file methods, so a failed operation leaves nothing behind in the temporary folder. The overwrite is best-effort: on a journalled or copy-on-write filesystem it is not guaranteed to reach the original blocks, so full-disk encryption remains the reliable protection.
- Neither direction can overwrite an existing item —
File_GetUniqueDesktopTarget and File_GetUniqueDesktopItem append -1, -2 … until the Desktop name is free, so encrypting or restoring can never destroy user data, and a restore cannot be used to clobber a chosen target file.
- Archive paths screened before expansion — because a
.ncryptaes file is authored by a peer rather than an automatically trusted party, File_ArchiveIsSafe parses the zip in pure Xojo (no plugin dependency) before FolderItem.UnZip runs: it walks the central directory for the authoritative entry list and screens both each central-directory name and its corresponding local-header name, so it does not matter which of the two the extractor trusts. The whole archive is refused if any name is absolute or contains a .. segment, or if it is malformed or ZIP64, so a hostile peer cannot use a crafted archive to write outside the private extraction folder (Zip Slip). The names are checked while the archive is still only on disk, so an unsafe path never reaches the extraction call.
- Size cap enforced before allocation — both file methods refuse anything above ~1 GB before reading it into memory, bounding the allocation a supplied file can force; the decrypt cap sits deliberately just above the encrypt cap so a legitimately encrypted item is not rejected on the way back.
- Single validation point per drop —
DropObject validates the shared secret once with the confirmation field matching before processing any item, so neither an encrypt nor a decrypt in a mixed drop can run against an unconfirmed secret.
- No Xojo plugin dependency; one audited native library, for licensing only — encryption, key derivation, and MAC verification rely entirely on Xojo's built-in
Crypto module, and the application uses no Xojo plugins at all. The single third-party native component in the build is libsodium (open-source, ISC-licensed), bundled solely to verify Ed25519 licence signatures. It is never invoked on the encryption, key-derivation, or MAC path, so the code that protects user data carries no third-party or commercial-license supply-chain surface; the only third-party code is a widely audited cryptographic library confined to reading a licence file.
- Clipboard plaintext is opt-in and self-clearing — copying decrypted text prompts for confirmation and auto-wipes the clipboard 30 seconds later via
Timer.CallLater, so plaintext never lands on the shared clipboard silently or lingers indefinitely.
- Passphrase deterministically zeroed at every call site —
AESCrypt and AESCryptData take the passphrase as a MemoryBlock that each of the four call sites ZeroBlocks immediately after use, matching the erasure already applied to the derived keys.
- Licence authenticity proven by an Ed25519 signature — an optional
NCryptAES_*.lic file placed beside the application is verified with libsodium's crypto_sign_open against a bundled Ed25519 public key before any "Licensed to" status is honoured. Only the public key ships in the build, so a valid licence cannot be forged, altered, or retargeted to another application without the private signing key, which never leaves the developer. Verification fails closed at every step: a library that will not load, an absent, truncated, or malformed licence, a signature that does not verify, or a licence issued for a different application all leave the app in its unlicensed trial state (with a single warning where appropriate) and offer no bypass. The signing library is loaded from a fixed location — an explicit @executable_path/../Resources path on macOS, and the executable's own directory (searched first for a normally installed app) on Windows — rather than from user-controlled input.
- Licence status gates no cryptographic operation — the outcome of licence verification changes only the window's trial/licensed chrome. It does not enable, disable, or alter encryption, key derivation, or MAC verification, so licence handling — successful, failed, forged, or absent — can never weaken or unlock the protection applied to user data.
User Security Advice
Steps you can take to get the most protection from NCryptAES
- Choose a strong, unique shared secret — all of NCryptAES's security rests on this secret. Use a long, random passphrase (toward the 64-character maximum) generated by a password manager, never one you reuse elsewhere, and avoid names, dates, and dictionary words. The secret is stretched with PBKDF2 at 800,000 iterations, but a weak or guessable secret can still be brute-forced.
- Share the secret over a separate channel — never send the shared secret in the same message, email, or transfer as the encrypted text or
.ncryptaes file. Agree it in person, by phone, or through a different secure app. Anyone who holds both the ciphertext and the secret can read everything.
- Turn on full-disk encryption — enable FileVault (macOS), BitLocker (Windows), or LUKS (Linux). While working with files, NCryptAES briefly writes plaintext to a temporary folder and holds it in memory, and on modern drives neither deletion nor memory release is guaranteed to erase those bytes. Full-disk encryption is the reliable protection against later recovery from the disk or swap.
- Lock your screen when you step away — the shared secret and any decrypted text clear themselves after five minutes of inactivity, and the on-screen
Idle Time Clear Down: ns counter shows how long remains. Even so, lock your computer whenever you leave it so nothing sensitive is on screen in the meantime.
- Be careful with the clipboard — copying decrypted text places plaintext on the system clipboard, which every application can read and which macOS Universal Clipboard or Windows Cloud Clipboard may sync to your other devices. NCryptAES asks first and wipes it after 30 seconds, but avoid copying sensitive plaintext at all, and turn off cross-device clipboard sync for confidential work.
- Only decrypt files from people you trust — a
.ncryptaes file can only be opened by someone who already has the shared secret, so its author is a peer you exchanged that secret with. Treat a file from any other source with suspicion and do not decrypt files whose origin you cannot vouch for.
- Protect the computer itself — keep your operating system and NCryptAES up to date, download the app only from its official source, and run it on a machine free of malware and keyloggers. Because the secret is typed in, a compromised computer defeats any encryption.
- Keep a safe, separate copy of the secret — there is no recovery mechanism and no backdoor: if you lose the shared secret, the encrypted data cannot be recovered. Store the secret securely, such as in a password manager, and keep it apart from the encrypted data itself.
- Remember the original still exists — encrypting a file does not remove its plaintext original. Delete originals you no longer need, and rely on full-disk encryption to protect them, since ordinary deletion can leave recoverable data on the drive.
Terminology
- Plaintext
- The readable, unencrypted data — the text you type or the original file — before NCryptAES protects it, and what you get back after a successful decrypt.
- Ciphertext
- The scrambled output produced by encryption. Without the shared secret it is indistinguishable from random noise and cannot be read.
- Shared secret (passphrase)
- The password both parties agree on. Everything NCryptAES protects rests on this one value: it is never stored, and the same secret used to encrypt must be supplied to decrypt.
- AES-256-CBC
- The encryption algorithm. AES (Advanced Encryption Standard) with a 256-bit key is the symmetric cipher; CBC (Cipher Block Chaining) is the mode that chains each block to the one before so identical blocks do not encrypt identically.
- Symmetric encryption
- A scheme where the same secret both locks and unlocks the data, as opposed to public-key systems that use a separate key for each direction.
- IV (Initialization Vector)
- A random 16-byte value mixed into the first block of CBC encryption. A fresh IV per encryption ensures the same plaintext and secret never produce the same ciphertext twice. It is not secret and is stored in the header.
- Salt
- A random 16-byte value combined with the passphrase before key derivation. It ensures two people using the same passphrase derive different keys and defeats pre-computed lookup tables. Like the IV, it is not secret and travels in the header.
- PBKDF2
- Password-Based Key Derivation Function 2 — the process that turns the passphrase into cryptographic keys by hashing it, together with the salt, many times over.
- Iteration count / key stretching
- The number of times PBKDF2 repeats its hashing (800,000 here). A high count makes each password guess deliberately slow, so brute-forcing a stolen file is expensive. This is called key stretching.
- Key separation
- Deriving two independent keys — one to encrypt, one to authenticate — rather than reusing a single key for both jobs, which is weaker cryptographic practice.
- HMAC-SHA256 (MAC)
- A Message Authentication Code: a keyed fingerprint of the data built on the SHA-256 hash. It proves the ciphertext was produced by someone who holds the secret and has not been altered in transit.
- Encrypt-then-MAC
- The order of operations used here: encrypt first, then compute the MAC over the result. On decrypt the MAC is checked before anything is decrypted, so tampered or forged data is rejected without being processed.
- Padding-oracle attack
- An attack against CBC in which an adversary learns the plaintext by submitting altered ciphertexts and observing whether the padding was valid. Verifying the MAC before decrypting closes this avenue.
- PKCS7 padding
- The standard scheme that pads the final block out to AES's fixed block size before encryption and is stripped after decryption.
- Constant-time comparison
- Comparing two values (such as MACs) in a way that always takes the same time regardless of where they differ, so an attacker cannot learn the correct value byte by byte from timing.
- Side-channel / timing attack
- An attack that infers secrets not from the algorithm itself but from indirect signals such as how long an operation takes. Constant-time comparison is a defence against the timing variety.
- Zeroing
- Deliberately overwriting keys, passphrases, and plaintext buffers in memory with zeros as soon as they are finished with, so they do not linger where they could later be recovered.
- Header / versioned container
- The fixed structure at the front of every encrypted output — a version byte, the iteration count, the salt, and the IV — all covered by the MAC. The version byte lets the format evolve while older files remain decryptable.
- Base64
- A text encoding that represents raw bytes using ordinary printable characters, so encrypted output from the Text tab can be safely pasted into email or chat. The file format skips this wrapper and stores the raw bytes.
- Zip Slip
- An attack where a malicious archive contains entry names like
../../file that, when extracted naively, write outside the intended folder. NCryptAES screens every archive path before extraction to prevent it.
- Full-disk encryption
- Operating-system encryption of the whole drive — FileVault, BitLocker, or LUKS — that protects data at rest, including temporary files and swap that individual applications cannot reliably erase.
- Digital signature (Ed25519)
- A cryptographic seal proving who produced a piece of data and that it has not been altered. The developer signs each licence with a private key; the app checks that signature with the matching public key. Ed25519 is a modern, fast elliptic-curve signature scheme. Here it authenticates the licence file — not the encrypted data, which is protected separately by the shared secret.
- Public / private key pair
- Two mathematically linked keys where one signs and the other verifies. The private (signing) key is kept secret by the developer; the public (verification) key can be shipped inside the application without weakening anything, because it can only check signatures, never create them.