File Encryption & Decryption
Lock any file with a password — big files too — and unlock it on any device.
Encrypted file
Password correct
Decrypted file
Decrypt without this website (Node.js script)
Save this script as decrypt-tvenc.mjs next to the file and run node decrypt-tvenc.mjs file.tvenc (Node.js 24.7 or later, no packages). It asks for the password, decrypts part by part and restores the original name.
// Decrypts a .tvenc file from MySmartCoPilot "File Encryption & Decryption" (TVF format, version 1).
// Needs Node.js 24.7 or later (crypto.argon2Sync); no packages. Reads and writes one 1 MiB part
// at a time, so files of any size work. Usage: node decrypt-tvenc.mjs file.tvenc [output-name]
// It asks for the password; what you type is not shown.
import { argon2Sync, createDecipheriv, pbkdf2Sync } from 'node:crypto';
import { closeSync, existsSync, fstatSync, openSync, readSync, renameSync, unlinkSync, writeSync } from 'node:fs';
import { basename } from 'node:path';
// Reads the password without showing it on screen (or one line, when it is piped in).
function askPassword(prompt) {
return new Promise((resolve) => {
const { stdin, stderr } = process;
const tty = stdin.isTTY;
let pw = '';
let done = false;
const finish = () => {
if (done) return;
done = true;
if (tty) stdin.setRawMode(false);
stdin.pause();
stderr.write('\n');
resolve(pw);
};
if (tty) stdin.setRawMode(true); // before the prompt, so that nothing typed is echoed
stderr.write(prompt);
stdin.setEncoding('utf8');
stdin.on('end', finish);
stdin.on('data', (chunk) => {
for (const c of chunk) {
if (done) return;
if (c === '\r' || c === '\n' || c === '\u0004') return finish(); // Enter (or Ctrl+D)
if (c === '\u0003') { if (tty) stdin.setRawMode(false); process.exit(130); } // Ctrl+C
pw = c === '\u007f' || c === '\b' ? [...pw].slice(0, -1).join('') : pw + c; // Backspace
}
});
});
}
const [input, outArg] = process.argv.slice(2);
const fd = openSync(input, 'r');
const read = (pos, len) => {
const b = Buffer.alloc(len);
if (readSync(fd, b, 0, len, pos) !== len) throw new Error('The file is cut off');
return b;
};
const first = read(0, 5);
if (first.toString('latin1', 0, 3) !== 'TVF' || first[3] !== 1) throw new Error('Not a TVF version 1 file');
const kdfLen = first[4] === 1 ? 5 : first[4] === 2 ? 7 : 0;
if (!kdfLen) throw new Error('Unknown key-derivation method');
const hlen = 4 + kdfLen + 16 + 7 + 8;
const header = read(0, hlen); // also the additional authenticated data
const o = 4 + kdfLen;
const salt = header.subarray(o, o + 16);
const prefix = header.subarray(o + 16, o + 23);
const partSize = header.readUInt32BE(o + 23);
const metaLen = header.readUInt32BE(o + 27);
const password = (await askPassword('Password: ')).normalize('NFC');
const key = first[4] === 1
? pbkdf2Sync(password, salt, header.readUInt32BE(5), 32, 'sha256')
: argon2Sync('argon2id', { message: password, nonce: salt, memory: header.readUInt32BE(5),
passes: header[9], parallelism: header[10], tagLength: 32 });
const open = (data, counter, flag) => {
const nonce = Buffer.alloc(12);
prefix.copy(nonce);
nonce.writeUInt32BE(counter, 7);
nonce[11] = flag; // 0 = part, 1 = last part, 2 = metadata
const d = createDecipheriv('aes-256-gcm', key, nonce);
d.setAAD(header);
d.setAuthTag(data.subarray(data.length - 16));
return Buffer.concat([d.update(data.subarray(0, data.length - 16)), d.final()]);
};
const meta = JSON.parse(open(read(hlen, metaLen), 0, 2).toString('utf8'));
const count = Math.max(1, Math.ceil(meta.size / partSize));
const name = outArg ?? (basename(String(meta.name)).replace(/[\u0000-\u001f]/g, '') || 'decrypted-file');
if (existsSync(name)) throw new Error(`${name} already exists: pass another output name`);
const tmp = `${name}.partial`;
const out = openSync(tmp, 'wx');
let pos = hlen + metaLen;
let done = false;
try {
for (let i = 0; i < count; i++) {
const last = i === count - 1;
const plainLen = last ? meta.size - i * partSize : partSize;
writeSync(out, open(read(pos, plainLen + 16), i, last ? 1 : 0));
pos += plainLen + 16;
}
if (fstatSync(fd).size !== pos) throw new Error('Unexpected data after the last part');
done = true;
} finally {
closeSync(out);
if (!done) unlinkSync(tmp);
}
renameSync(tmp, name);
console.error(`Decrypted ${meta.size} bytes to ${name}`);
About the File Encryption & Decryption
Encrypt any file — a scanned ID, a spreadsheet, a photo archive, a multi-gigabyte video — with a password, right in your browser. The file is read and encrypted one 1 MiB part at a time with AES-256-GCM under a key derived from your password with Argon2id (or PBKDF2), so even very large files never have to fit in memory, and nothing is uploaded.
The result is a single .tvenc file in a documented format. Every part is authenticated and numbered, so a wrong password, a changed byte, a missing part or a file cut short by an unfinished download is detected — and nothing is saved instead of a corrupt file. The original name and type are stored encrypted inside and restored when you decrypt, on this page in any modern browser or with the short Node.js script provided here.
How to use it
- Under Encrypt, choose or drop a file of any type and size.
- Enter a password twice, or press Suggest a strong password, and store it safely — it cannot be recovered.
- Optionally tick Hide the file name (the .tvenc file then gets a neutral name) and, in Chrome or Edge, Choose where to save to write very large files straight to disk.
- Press Encrypt file, then Download the .tvenc file (or find it where you chose to save it). Share the password separately from the file.
- To open it, choose Decrypt, pick the .tvenc file, enter the password and press Decrypt file. Once the password is confirmed, save the restored file under its original name.
Examples
passport.pdf (1.8 MB) · Argon2id 64 MiB · password from Suggest
passport.pdf.tvenc — about 150 bytes larger than the original
The overhead is the header (40–42 bytes), the encrypted name and size, and a 16-byte tag per 1 MiB part.
holiday.mp4 (4 GB) in Chrome on a computer, with Choose where to save
holiday.mp4.tvenc, written part by part into the folder you chose
The video is never loaded into memory; progress and speed are shown, and Cancel discards the unfinished file.
Tax return 2026.pdf with Hide the file name
encrypted-20261004-0905.tvenc — decrypting restores “Tax return 2026.pdf”
Common uses
- Protecting ID scans, tax papers, medical reports and contracts before e-mailing them or putting them in cloud storage.
- Encrypting backups — photo archives, password-manager exports, database dumps — before copying them to a USB drive or another computer.
- Sending a file through a chat app or file-transfer service that you do not want to be able to read it.
- Keeping one or two private files encrypted on a shared or family computer.
How a file is protected
- Key. Your password (UTF-8, NFC-normalised) and a new random 16-byte salt give a 256-bit key through Argon2id (RFC 9106; 64 MiB, 3 passes, 4 lanes by default) or PBKDF2-HMAC-SHA256 with 600,000 iterations (the OWASP Password Storage Cheat Sheet figure). Every file gets its own salt, so its own key.
- Parts. The file is split into 1 MiB parts, each encrypted with AES-256-GCM (NIST SP 800-38D). The 96-bit nonce of each part is a random 7-byte prefix, the part number and a flag that marks the last part — the STREAM construction of Hoang, Reyhanitabar, Rogaway and Vizár (IACR ePrint 2015/189). Swapping, dropping or repeating parts, or cutting the file at a part boundary, makes a check fail.
- Metadata. The original name, size and type are encrypted as their own small block, which is checked first: a wrong password is reported before anything is decrypted, and a file that is shorter or longer than it should be is refused before a byte is written.
- Header. The format version, key-derivation settings, salt, nonce prefix and part size are authenticated with every part (GCM additional data), so they cannot be altered either.
The .tvenc format (TVF, version 1)
All numbers are big-endian:
- Header:
TVFand version01; the key-derivation block (01+ 4-byte PBKDF2 iteration count, or02+ 4-byte Argon2id memory in KiB, 1-byte passes, 1-byte lanes); 16-byte salt; 7-byte nonce prefix; 4-byte part size C; 4-byte metadata length L. - Metadata: L bytes — AES-256-GCM of the JSON
{"name","size","type"}with nonce = prefix ‖00000000‖02. - Parts: part i is AES-256-GCM of plaintext bytes i·C to (i+1)·C with nonce = prefix ‖ i (4 bytes) ‖
01for the last part or00for the others. All parts but the last hold exactly C bytes plus a 16-byte tag; an empty file is one empty last part. - The additional authenticated data of the metadata and of every part is the whole header.
Very large files
The input is always read part by part. Where the output goes depends on the browser (support from MDN’s compatibility data for showSaveFilePicker and createWritable):
- Chrome, Edge and Opera on a computer, and Chrome on Android 132 or later can save straight into a file you choose — no copy in memory, no size limit other than your disk.
- Other current browsers (Firefox 111+, Safari 26+, older Chrome on Android) build the result in the browser’s private storage on disk, then download it. That needs free disk space about the size of the file. The copy is removed when you encrypt or decrypt the next file or press Reset; a copy left by a tab you closed is removed the next time the page is opened.
- Where neither is available (for example some private-browsing windows), the result is kept in memory until you save it, so the largest file depends on the device’s memory.
Decrypting without this website
Under the tool, Decrypt without this website has a Node.js script (Node.js 24.7 or later, no packages) that decrypts a .tvenc file part by part, restores its name and refuses damaged files without leaving a partial output. This tool’s tests run that exact script against files made here. The format above is enough to write a decryptor in any language with AES-GCM and Argon2id or PBKDF2.
Limitations
- A forgotten password cannot be recovered — not by MySmartCoPilot, not by anyone.
- One file at a time. To protect a folder, pack it into a ZIP first, or use the Password-Protected ZIP tool, which encrypts many files at once.
- The size of the .tvenc file shows roughly how big the original is. Unless you hide it, the original name is also visible in the .tvenc file name.
- Only .tvenc files made here can be decrypted. ZIP, 7-Zip, PGP, OpenSSL
encand age files use other formats; for PGP use PGP Encrypt / Decrypt. - Speed depends on the device. Before the first part is written, Argon2id needs 64 MiB of memory and under a second on a recent laptop (measured in Chrome); older phones take longer.
Privacy
Files are read from your device by your browser and encrypted with its Web Crypto; Argon2id runs in a background worker. Nothing is uploaded, logged or kept on a server. Temporary copies in the browser’s private storage stay on your device and are removed when you process the next file or press Reset — a copy left by a tab you closed, the next time this page is opened. Only your choice of key setting is remembered.
Frequently asked questions
Is my file uploaded to MySmartCoPilot?
No. The file never leaves your device: it is read and encrypted in this browser tab, and the page keeps working with the network switched off once it has loaded.
Is there a file size limit?
No fixed limit. Where the browser can save straight to disk (Chrome, Edge, Opera on a computer; Chrome on Android 132+), size is limited only by your disk. Elsewhere the result is first built in the browser’s private storage, which needs free space about the size of the file.
What happens if the encrypted file is damaged or someone changes it?
Each 1 MiB part carries its own authentication tag and position, and the stored size is checked first. Any change, a missing or reordered part, or a file cut short is detected, the error says which part failed, and the unfinished output is discarded — you never get a silently corrupted file.
Can I decrypt the file on another computer or phone?
Yes. Open this page in any current browser — no account, nothing to install — choose Decrypt, pick the .tvenc file and enter the password. Without internet access you can use the Node.js script from this page.
Why not just use a password-protected ZIP?
A ZIP with AES encryption opens in archive programs such as 7-Zip, which suits recipients who will not use this page. But the WinZip AES format encrypts only the file contents — names, sizes and dates stay readable — and derives its key with PBKDF2-HMAC-SHA1 and 1,000 iterations (WinZip’s AES specification). A .tvenc file uses memory-hard Argon2id or 600,000 PBKDF2-SHA256 iterations and can hide the file name.
Why does it ask for the password before letting me save?
Decrypting first checks the password against the encrypted name and size. Only when that succeeds does the page show the original name and let you choose where to save it, so a typo never leaves you with an empty or broken file.