@h3l1os/mp4vault 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # @h3l1os/mp4vault
2
+
3
+ Hide and extract files within MP4 video containers with optional AES-256-GCM encryption.
4
+
5
+ ## Features
6
+
7
+ - Embed any file (text, images, binaries) inside an MP4 container
8
+ - AES-256-GCM authenticated encryption with key or password
9
+ - Mix public and encrypted files in the same container
10
+ - Attach metadata to embedded files
11
+ - Preserves MP4 playability — output files remain valid MP4 videos
12
+ - Dual ESM/CJS output
13
+ - Node.js 18+
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @h3l1os/mp4vault
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Embed a file with key encryption
24
+
25
+ ```typescript
26
+ import { MP4, Convert, Writable } from '@h3l1os/mp4vault';
27
+
28
+ const key = Convert.hexStringToBuffer('000102030405060708090a0b0c0d0e0f');
29
+
30
+ const mp4 = new MP4();
31
+ mp4.setKey(key);
32
+ await mp4.loadFile({ filename: 'video.mp4' });
33
+
34
+ await mp4.embedFile({ filename: 'secret.pdf' });
35
+
36
+ const writable = new Writable({ filename: 'output.mp4' });
37
+ await mp4.embed(writable);
38
+ ```
39
+
40
+ ### Embed a file with password encryption
41
+
42
+ ```typescript
43
+ const mp4 = new MP4();
44
+ mp4.setPassword('my-secret-password');
45
+ await mp4.loadFile({ filename: 'video.mp4' });
46
+
47
+ await mp4.embedFile({ filename: 'secret.pdf' });
48
+
49
+ const writable = new Writable({ filename: 'output.mp4' });
50
+ await mp4.embed(writable);
51
+ ```
52
+
53
+ ### Embed without encryption
54
+
55
+ ```typescript
56
+ const mp4 = new MP4();
57
+ await mp4.loadFile({ filename: 'video.mp4' });
58
+
59
+ await mp4.embedFile({ filename: 'document.txt' });
60
+
61
+ const writable = new Writable({ filename: 'output.mp4' });
62
+ await mp4.embed(writable);
63
+ ```
64
+
65
+ ### Mix public and encrypted files
66
+
67
+ ```typescript
68
+ const mp4 = new MP4();
69
+ mp4.setKey(key);
70
+ await mp4.loadFile({ filename: 'video.mp4' });
71
+
72
+ // Public file (opt out of encryption with password: null)
73
+ await mp4.embedFile({ filename: 'readme.txt', password: null });
74
+
75
+ // Encrypted file (uses the key set on the MP4 instance)
76
+ await mp4.embedFile({ filename: 'secret.pdf' });
77
+
78
+ const writable = new Writable({ filename: 'output.mp4' });
79
+ await mp4.embed(writable);
80
+ ```
81
+
82
+ ### Embed with metadata
83
+
84
+ ```typescript
85
+ await mp4.embedFile({
86
+ filename: 'photo.jpg',
87
+ meta: { author: 'alice', tags: ['vacation', '2026'] },
88
+ });
89
+ ```
90
+
91
+ ### Extract files
92
+
93
+ ```typescript
94
+ import { MP4, Convert, Writable } from '@h3l1os/mp4vault';
95
+
96
+ const key = Convert.hexStringToBuffer('000102030405060708090a0b0c0d0e0f');
97
+
98
+ const mp4 = new MP4();
99
+ mp4.setKey(key);
100
+ await mp4.loadFile({ filename: 'output.mp4' });
101
+
102
+ // List embedded files
103
+ const files = mp4.getEmbedFiles();
104
+ console.log(files);
105
+ // [{ filename: 'secret.pdf', size: 12345, isEncrypted: true, offset: 0 }]
106
+
107
+ // Extract by index
108
+ const extracted = await mp4.extractFile(0);
109
+ await (extracted as Writable).saveToFile('restored-secret.pdf');
110
+ ```
111
+
112
+ ### Get expected output size
113
+
114
+ ```typescript
115
+ const mp4 = new MP4();
116
+ mp4.setKey(key);
117
+ await mp4.loadFile({ filename: 'video.mp4' });
118
+ await mp4.embedFile({ filename: 'secret.pdf' });
119
+
120
+ const expectedBytes = await mp4.getExpectedSize();
121
+ ```
122
+
123
+ ## API
124
+
125
+ ### `MP4`
126
+
127
+ | Method | Description |
128
+ |--------|-------------|
129
+ | `setKey(key: Buffer)` | Set AES encryption key (16, 24, or 32 bytes) |
130
+ | `setPassword(password: string)` | Set password for PBKDF2 key derivation |
131
+ | `loadFile({ filename })` | Parse an MP4 file |
132
+ | `embedFile({ filename, meta?, key?, password? })` | Add a file to embed. Pass `password: null` or `key: null` to opt out of encryption for this file |
133
+ | `embed(writable?)` | Write the MP4 with embedded data |
134
+ | `getExpectedSize()` | Get the expected output size in bytes |
135
+ | `getEmbedFiles()` | List files embedded in the loaded MP4 |
136
+ | `extractFile(index, writable?)` | Extract an embedded file by index |
137
+ | `findAtom(name)` | Find a top-level atom by name |
138
+ | `findAtoms(atoms, name)` | Recursively find atoms by name |
139
+
140
+ ### `Convert`
141
+
142
+ | Method | Description |
143
+ |--------|-------------|
144
+ | `hexStringToBuffer(hex)` | Convert a hex string to a Buffer (validates input) |
145
+ | `objectToBuffer(obj)` | Serialize an object to a Buffer (JSON) |
146
+ | `bufferToObject(buf)` | Deserialize a Buffer to an object |
147
+
148
+ ### `Writable`
149
+
150
+ | Method | Description |
151
+ |--------|-------------|
152
+ | `new Writable({ filename? })` | Create a writable (file or in-memory) |
153
+ | `saveToFile(filename)` | Save in-memory contents to a file |
154
+ | `toReadable()` | Convert to a Readable for re-processing |
155
+ | `size()` | Get bytes written |
156
+
157
+ ## Security
158
+
159
+ - **AES-256-GCM** authenticated encryption (detects tampering)
160
+ - **PBKDF2** key derivation with 600,000 iterations and SHA-512 for password-based encryption
161
+ - Random 12-byte IV and 16-byte salt per encryption operation via `crypto.randomBytes()`
162
+ - Uses Node.js native `crypto` module — no third-party crypto dependencies
163
+
164
+ ## Binary format
165
+
166
+ Embedded data is placed at the start of the `mdat` atom payload:
167
+
168
+ ```
169
+ [public header][encrypted header][file1 data][file2 data]...
170
+ ```
171
+
172
+ Each header and file follows this format:
173
+
174
+ - **Unencrypted**: `[flag 1B][type 1B][payload]`
175
+ - **Encrypted**: `[flag 1B][type 1B][salt 16B][IV 12B][ciphertext][authTag 16B]`
176
+
177
+ Sample offsets in `stco`/`co64` atoms are adjusted to account for the inserted data.
178
+
179
+ ## Development
180
+
181
+ ```bash
182
+ npm install
183
+ npm run build # Build ESM + CJS with tsup
184
+ npm run typecheck # TypeScript strict mode check
185
+ npm test # Run all tests with vitest
186
+ npm run test:watch # Watch mode
187
+ ```
188
+
189
+ ### Test suite
190
+
191
+ - **32 tests** across 7 test files
192
+ - Unit tests: AES encryption, Pack binary operations, Convert utilities
193
+ - Integration tests: EmbedBinary, EmbedObject, EmbedMeta
194
+ - End-to-end tests: full embed/extract cycles with key, password, no encryption, mixed files, binary files, metadata, re-embedding, size validation, wrong key rejection
195
+
196
+ ## Project structure
197
+
198
+ ```
199
+ src/
200
+ index.ts # Public API exports
201
+ MP4.ts # MP4 parser, embedder, extractor
202
+ Atom.ts # MP4 atom/box representation
203
+ AES.ts # AES-256-GCM encryption
204
+ Embed.ts # Embedding coordinator
205
+ EmbedBinary.ts # Binary file embedding
206
+ EmbedObject.ts # JSON object embedding
207
+ Convert.ts # Buffer/hex/JSON utilities
208
+ Pack.ts # Binary pack/unpack (struct encoding)
209
+ jspack.js # Vendored jspack library
210
+ constants.ts # Buffer size, max header, max int32
211
+ types.ts # IReadable, IWritable, FileRecord interfaces
212
+ utils.ts # Temp file helper
213
+ node/
214
+ Readable.ts # Node.js file reader
215
+ Writable.ts # Node.js file writer
216
+ test/
217
+ common.test.ts # AES, Convert, Pack unit tests
218
+ embedBinary.test.ts
219
+ embedObject.test.ts
220
+ embedMeta.test.ts
221
+ mixed.test.ts
222
+ mp4.test.ts
223
+ e2e.test.ts # End-to-end embed/extract tests
224
+ ```
225
+
226
+ ## License
227
+
228
+ [AGPL-3.0](LICENSE)