Skip to content

Commit 22459b0

Browse files
committed
0.2.2: Error Logic, New APIs, EngineController, Example
Error Logic - Implemented custom errors for encryption / key derivation / header logic - CLI and plugin now returns only the error no stacktrace - 5 custom error classes Verbosity: - Up to 4 verbosity levels for cli and browser - logging in browser and console based on level CLI - decode command to show difficulty, version, salt lenght and value via input file or with stdin -input validation Decrypting - now tries to use a different version engine when set and available based on header data Added Browser example in `/examples/index.html` Made `utils/bytes.ts` browser compatible Removed unnecessary wasm files Updated README.md
1 parent f8d2ace commit 22459b0

37 files changed

Lines changed: 1332 additions & 535 deletions

File tree

README.md

Lines changed: 88 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
11
# @mqxym/cryptit
22

3-
Modern, cross-platform AES-GCM + Argon2-id encryption for files **and** text.
3+
Modern, crossplatform **AESGCM 256 + Argon2‑id** encryption for both **files** *and* **text**.
44

5-
* **Node 18+ / Bun 1+** – uses the native `argon2` addon & WebCrypto
6-
* **Browser (evergreen)** – loads the tiny `argon2-browser` WASM module
7-
* **CLI** – stream-encrypt large files without loading them in memory
8-
* **TypeScript-first**, tree-shakable, ESM & CommonJS builds.
5+
* **Node 18 / Bun 1** – native `argon2` addon + WebCrypto
6+
* **Browser (evergreen)** – tiny WASM build of `argon2-browser`
7+
* **CLI** – stream encryption & decryption, zero memory bloat
8+
* **TypeScript‑first**, tree‑shakable, ESM & CJS builds
9+
* **Format‑agnostic decryption** – one instance reads any registered version
910

1011
---
1112

1213
## Install
1314

1415
```bash
15-
# with Bun (recommended)
16+
# Bun (recommended)
1617
bun add @mqxym/cryptit
1718

18-
# …or with npm / pnpm
19-
npm i @mqxym/cryptit
19+
# npm / pnpm
20+
yarn add @mqxym/cryptit # or npm i / pnpm add
2021
```
2122

2223
---
2324

24-
## Quick start (Node / Bun)
25+
## Quick start – Node / Bun
2526

2627
```ts
2728
import { createCryptit } from "@mqxym/cryptit";
2829

2930
const crypt = createCryptit({ difficulty: "middle" });
30-
const passphrase = "correct horse battery staple";
31+
const pass = "correct horse battery staple";
3132

32-
const cipherB64 = await crypt.encryptText("hello world", passphrase);
33-
const plainTxt = await crypt.decryptText(cipherB64, passphrase);
34-
console.log(plainTxt); // → "hello world"
33+
const b64 = await crypt.encryptText("hello", pass);
34+
console.log(await crypt.decryptText(b64, pass)); // → "hello"
3535
```
3636

37-
### Encrypt / decrypt a file (streaming)
37+
### Streaming files
3838

3939
```ts
4040
import { createCryptit } from "@mqxym/cryptit";
@@ -43,134 +43,124 @@ import { createReadStream, createWriteStream } from "node:fs";
4343
const crypt = createCryptit();
4444
const pass = "hunter2";
4545

46-
const rs = createReadStream("movie.mkv");
47-
const ws = createWriteStream("movie.enc");
48-
49-
await rs
46+
// encrypt → movie.enc
47+
await createReadStream("movie.mkv")
5048
.pipeThrough(await crypt.createEncryptionStream(pass))
51-
.pipeTo(ws);
52-
53-
// ——— later ———
54-
const drs = createReadStream("movie.enc");
55-
const dws = createWriteStream("movie.mkv");
49+
.pipeTo(createWriteStream("movie.enc"));
5650

57-
await drs
51+
// decrypt back
52+
await createReadStream("movie.enc")
5853
.pipeThrough(await crypt.createDecryptionStream(pass))
59-
.pipeTo(dws);
54+
.pipeTo(createWriteStream("movie.mkv"));
6055
```
6156

6257
---
6358

6459
## Browser usage
6560

66-
### Bundle with Vite / Webpack / esbuild
67-
68-
```ts
69-
// app.ts
70-
import { createCryptit } from "@mqxym/cryptit/browser";
71-
72-
const crypt = createCryptit({ saltStrength: "high" });
73-
74-
input.addEventListener("change", async (e) => {
75-
const file = (e.target as HTMLInputElement).files![0];
76-
const blob = await crypt.encryptFile(file, "mypw");
77-
downloadBlob(blob, file.name + ".enc");
78-
});
79-
```
61+
```html
62+
<!-- app.ts / app.js -->
63+
<script type="module">
64+
import { createCryptit } from "@mqxym/cryptit/browser";
8065
81-
> The `argon2-browser` WASM file is fetched lazily.
82-
> Host it yourself:
83-
>
84-
> ```ts
85-
> import * as a2 from "argon2-browser";
86-
> a2.wasmURL = "/static/argon2.wasm";
87-
> ```
66+
// IMPORTANT: host argon2.wasm at /dist/argon2.wasm (relative to final HTML)
8867
89-
### Script-tag fallback
68+
const crypt = createCryptit({ saltStrength: "high", verbose: 2 });
9069
91-
```html
92-
<script src="https://unpkg.com/@mqxym/cryptit/dist/browser.min.js"></script>
93-
<script>
94-
const crypt = Cryptit.createCryptit();
95-
crypt.encryptText("hi", "pw").then(console.log);
70+
async function enc() {
71+
const cipher = await crypt.encryptText("hello", "pw");
72+
console.log(cipher);
73+
}
74+
enc();
9675
</script>
9776
```
9877

99-
---
78+
*Use with a bundler or simply via `<script type="module">`.*
10079

101-
## CLI
80+
---
10281

103-
```bash
104-
# one-off use
105-
bunx @mqxym/cryptit encrypt notes.txt -o notes.enc -p "pw"
82+
## API highlights
10683

107-
# or install globally
108-
bun add -g @mqxym/cryptit # -> `cryptit` in $PATH
84+
```ts
85+
const c = createCryptit({ verbose: 1 });
10986

110-
# encrypt file
111-
cat photo.jpg | cryptit encrypt - -p hunter2 > photo.enc
87+
// convenience 🔒/🔓
88+
await c.encryptText("txt", pass);
89+
await c.decryptText(b64, pass);
11290

113-
# decrypt
114-
cryptit decrypt photo.enc -p hunter2 -o photo.jpg
91+
// runtime tweaks
92+
c.setDifficulty("high");
93+
c.setVersion(2); // choose another registered format
94+
c.setSaltLength(32);
11595

116-
# encrypt text
117-
echo "secret" | cryptit encrypt-text -p pw
96+
// helpers
97+
Cryptit.isEncrypted(blobOrB64); // ↦ boolean
98+
Cryptit.headerDecode(blobOrB64); // ↦ meta {version, salt, …}
11899
```
119100

120-
| Flag | Default | Notes |
121-
| -------------------------- | -------- | ------------------------------------- |
122-
| `-p, --pass` | *prompt* | passphrase (hidden prompt if omitted) |
123-
| `-d, --difficulty` | middle | Argon2 preset: `low / middle / high` |
124-
| `-s, --salt-strength` | high | `low` (8 B) or `high` (16 B) salt |
125-
| `-c, --chunk-size <bytes>` | 524288 | size of plaintext blocks |
126-
| `-v, -vv, …` | 0 | increase verbosity |
101+
Verbose levels:
127102

128-
Exit codes: **0** ok · **10** auth failure · **11** invalid header · **≥20** other error
103+
| Level | Emits |
104+
| ----- | ----------------------------- |
105+
| 0 | errors only |
106+
| 1 | +start/finish notices |
107+
| 2 | +timings, key‑derivation info |
108+
| 3 | +salt / version / KDF details |
109+
| 4 | wire‑level debug |
129110

130111
---
131112

132-
## API surface
113+
## CLI (`cryptit`)
133114

134-
```ts
135-
createCryptit(cfg?: EncryptionConfig) → Cryptit
115+
```bash
116+
# encrypt file → .enc | decrypt back
117+
encrypt: cryptit encrypt <in> [-o out] [options]
118+
decrypt: cryptit decrypt <in> [-o out] [options]
136119

137-
interface EncryptionConfig {
138-
difficulty?: "low" | "middle" | "high"
139-
saltStrength?: "low" | "high"
140-
chunkSize?: number // default 512 KiB
141-
}
120+
encrypt text : echo "secret" | cryptit encrypt-text -p pw
121+
decrypt text : echo "…b64…" | cryptit decrypt-text -p pw
142122

143-
class Cryptit {
144-
encryptText(plain, pass): Promise<string>
145-
decryptText(b64, pass): Promise<string>
123+
# inspect header (no decryption)
124+
cryptit decode movie.enc
125+
cat movie.enc | cryptit decode
126+
```
146127

147-
encryptFile(file: Blob, pass): Promise<Blob>
148-
decryptFile(file: Blob, pass): Promise<Blob>
128+
### Common flags
149129

150-
createEncryptionStream(pass): Promise<TransformStream>
151-
createDecryptionStream(pass): Promise<TransformStream>
152-
}
153-
```
130+
| Flag | Default | Description |
131+
| ------------------------- | ------- | -------------------- |
132+
| `-p, --pass <pw>` | prompt | passphrase |
133+
| `-d, --difficulty <l>` | middle | Argon2 preset |
134+
| `-s, --salt-strength <l>` | high | 8 B vs 16 B salt |
135+
| `-c, --chunk-size <n>` | 524 288 | plaintext block size |
136+
| `-v, --verbose` |  0 … 4 | repeat to increase |
137+
138+
Exit codes: **0** success · **1** any failure (invalid header, auth, I/O …)
139+
140+
---
141+
142+
## Versioned format
143+
144+
* Header: `0x01 | infoByte | salt`
145+
* Decryptors pick the engine by the header’s version ⇒ **one CLI handles all registered versions**.
154146

155147
---
156148

157-
## Building from source
149+
## Build from source
158150

159151
```bash
160152
git clone https://github.com/mqxym/cryptit
161153
cd cryptit
162-
bun install
163-
bun run build # emits dist/ + native CLI binary
164-
bun test # Bun test runner with coverage
154+
bun install && bun run build && bun test
165155
```
166156

167157
---
168158

169-
## Security notes
159+
## Security
170160

171-
* AES-GCM 256 with 12-byte IV, 128-bit tag
172-
* Argon2-id defaults: `middle` = `t=3`, `memory=64 MiB`
173-
* Keys derived & held in-memory only; secrets zeroed where possible
161+
* AESGCM 256 / 12byte IV / 128bit tag
162+
* Argon2id presets (low / middle / high)
163+
* Salts generated per‑ciphertext; never reused
174164

175165
---
176166

bun.build.js

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { $ } from "bun";
2-
import { resolve } from "node:path";
2+
import { resolve, join, dirname } from 'node:path';
33

44
const outdir = "dist";
55

@@ -16,6 +16,7 @@ await runBuild(
1616
entryNode,
1717
"--minify",
1818
"--format=esm",
19+
"--external:argon2-browser",
1920
"--target=node",
2021
`--asset-naming=cryptit.index.[ext]`,
2122
"--sourcemap=external",
@@ -28,28 +29,17 @@ await runBuild(
2829
"--format=cjs",
2930
"--target=node",
3031
"--sourcemap=external",
32+
"--external:argon2-browser",
3133
`--asset-naming=cryptit.index.[ext]`,
3234
`--outdir=${resolve(outdir, "cryptit.index.cjs")}`
3335
);
3436

35-
await runBuild(
36-
entryBrowser,
37-
"--minify",
38-
"--target=browser",
39-
"--format=esm",
40-
"--sourcemap=external",
41-
"--external:commander",
42-
"--external:argon2",
43-
"--scope-hoist",
44-
`--asset-naming=cryptit.browser.min.[ext]`,
45-
`--outdir=${resolve(outdir, "cryptit.browser.min.js")}`
46-
);
47-
4837
await runBuild(
4938
entryCLI,
5039
"--minify",
5140
"--format=cjs",
5241
"--target=node",
42+
"--external:argon2-browser",
5343
"--sourcemap=external",
5444
`--outdir=${resolve(outdir, "cryptit.cli.cjs")}`,
5545
`--asset-naming=cryptit.cli.[ext]`
@@ -60,6 +50,7 @@ await runBuild(
6050
"--minify",
6151
"--format=esm",
6252
"--target=node",
53+
"--external:argon2-browser",
6354
"--sourcemap=external",
6455
`--outdir=${resolve(outdir, "cryptit.cli.mjs")}`,
6556
`--asset-naming=cryptit.cli.[ext]`
@@ -68,9 +59,42 @@ await runBuild(
6859
await runBuild(
6960
entryCLI,
7061
"--compile",
62+
"--external:argon2-browser",
7163
`--outfile=${resolve(outdir, "bin", "cryptit")}`
7264
);
7365

66+
await runBuild(
67+
[entryBrowser],
68+
"--minify",
69+
"--target=browser",
70+
"--format=esm",
71+
"--sourcemap=external",
72+
"--external:commander",
73+
"--external:buffer",
74+
"--external:process",
75+
`--asset-naming=cryptit.browser.min.[ext]`,
76+
`--outdir=${resolve(outdir, "cryptit.browser.min.js")}`
77+
);
78+
7479
async function runBuild(entryFile, ...flags) {
7580
await $`bun build ${entryFile} ${flags}`;
7681
}
82+
83+
import { copyFileSync } from 'node:fs';
84+
85+
const outDir = resolve(__dirname, 'dist', 'cryptit.browser.min.js');
86+
87+
const srcWasm = join(outDir, 'cryptit.browser.min.wasm');
88+
const dstWasm = join(outDir, 'argon2.wasm');
89+
const dstWasm2 = join('examples', 'dist', 'argon2.wasm')
90+
91+
copyFileSync(srcWasm, dstWasm);
92+
copyFileSync(srcWasm, dstWasm2);
93+
94+
await $`rm -rf ${join(outDir, 'cryptit.browser.min.wasm')}`;
95+
await $`rm -rf ${join(outdir, 'cryptit.cli.cjs', 'cryptit.cli.wasm')}`;
96+
await $`rm -rf ${join(outdir, 'cryptit.cli.mjs', 'cryptit.cli.wasm')}`;
97+
await $`rm -rf ${join(outdir, 'cryptit.index.mjs', 'cryptit.index.wasm')}`;
98+
await $`rm -rf ${join(outdir, 'cryptit.index.cjs', 'cryptit.index.wasm')}`;
99+
100+

bun.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
File renamed without changes.

dist/cryptit.browser.min.js/index.js

Lines changed: 37 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)