wilcocrypt 2.1.0 → 2.2.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/.github/ISSUE_TEMPLATE/bug.md +78 -0
- package/.github/ISSUE_TEMPLATE/config.yml +6 -0
- package/.github/ISSUE_TEMPLATE/feature.yml +38 -0
- package/.github/dependabot.yml +11 -0
- package/CHANGELOG.md +62 -0
- package/DOCS.md +335 -0
- package/README.md +69 -59
- package/SECURITY.md +48 -0
- package/package.json +17 -10
- package/src/cli.js +141 -0
- package/src/wilcocrypt.js +422 -0
- package/types/wilcocrypt.d.ts +186 -0
- package/dist/cli.js +0 -4441
- package/dist/cli.min.cjs +0 -2
- package/dist/cli.min.js +0 -2
- package/dist/wilcocrypt.js +0 -987
- package/dist/wilcocrypt.min.js +0 -1
- package/rollup.config.js +0 -54
- package/sea/sea-config.json +0 -5
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { randomBytes, scryptSync, createCipheriv, createDecipheriv } from 'crypto';
|
|
2
|
+
import { gzipSync, gunzipSync, createGzip, createGunzip } from 'zlib';
|
|
3
|
+
import { readFileSync, writeFileSync, createReadStream, createWriteStream, promises as fsPromises } from 'fs';
|
|
4
|
+
import { pipeline } from 'stream/promises';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Main WilcoCrypt namespace.
|
|
8
|
+
*/
|
|
9
|
+
const wilcocrypt = {};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal WilcoCrypt utilities and constants.
|
|
13
|
+
*/
|
|
14
|
+
wilcocrypt._ = {};
|
|
15
|
+
|
|
16
|
+
/* =========================
|
|
17
|
+
Custom Error
|
|
18
|
+
========================= */
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Custom error class for all WilcoCrypt-specific errors.
|
|
22
|
+
*/
|
|
23
|
+
class WilcoCryptError extends Error {
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} message - Human-readable error message
|
|
26
|
+
* @param {string} [code=WILCOCRYPT_ERROR] - Machine-readable error code
|
|
27
|
+
*/
|
|
28
|
+
constructor (message, code = 'WILCOCRYPT_ERROR') {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'WilcoCryptError';
|
|
31
|
+
this.code = code;
|
|
32
|
+
|
|
33
|
+
if (Error.captureStackTrace) {
|
|
34
|
+
Error.captureStackTrace(this, WilcoCryptError);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
wilcocrypt._.WilcoCryptError = WilcoCryptError;
|
|
40
|
+
|
|
41
|
+
/* =========================
|
|
42
|
+
Internal constants
|
|
43
|
+
========================= */
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Internal WilcoCrypt version.
|
|
47
|
+
* Must match exactly during decryption.
|
|
48
|
+
* @type {string}
|
|
49
|
+
*/
|
|
50
|
+
wilcocrypt._.VERSION = '2.2.0';
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Minimum allowed password length.
|
|
54
|
+
* @type {number}
|
|
55
|
+
*/
|
|
56
|
+
wilcocrypt._.MIN_PASSWORD_LENGTH = 6;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Internal header for encrypted payloads.
|
|
60
|
+
* @type {Buffer}
|
|
61
|
+
*/
|
|
62
|
+
wilcocrypt._.HEADER = Buffer.from([23, 9, 12, 3, 15, 3, 18, 25, 16, 20]);
|
|
63
|
+
|
|
64
|
+
/* =========================
|
|
65
|
+
Internal helpers
|
|
66
|
+
========================= */
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validates AES-256-GCM key and IV.
|
|
70
|
+
*
|
|
71
|
+
* @param {Buffer} key
|
|
72
|
+
* @param {Buffer} iv
|
|
73
|
+
* @throws {WilcoCryptError}
|
|
74
|
+
*/
|
|
75
|
+
wilcocrypt._.assertKeyAndIv = function (key, iv) {
|
|
76
|
+
if (!Buffer.isBuffer(key) || key.length !== 32) {
|
|
77
|
+
throw new WilcoCryptError(
|
|
78
|
+
'Invalid encryption key (expected 32-byte Buffer)',
|
|
79
|
+
'INVALID_KEY'
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!Buffer.isBuffer(iv) || iv.length !== 12) {
|
|
84
|
+
throw new WilcoCryptError(
|
|
85
|
+
'Invalid IV (expected 12-byte Buffer)',
|
|
86
|
+
'INVALID_IV'
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validates password strength.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} password
|
|
95
|
+
* @throws {WilcoCryptError}
|
|
96
|
+
*/
|
|
97
|
+
wilcocrypt._.assertPassword = function (password) {
|
|
98
|
+
if (typeof password !== 'string' || password.length < wilcocrypt._.MIN_PASSWORD_LENGTH) {
|
|
99
|
+
throw new WilcoCryptError(
|
|
100
|
+
`Password must be at least ${wilcocrypt._.MIN_PASSWORD_LENGTH} characters`,
|
|
101
|
+
'WEAK_PASSWORD'
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Constant-time buffer comparison.
|
|
108
|
+
* Reserved for future extensions.
|
|
109
|
+
*
|
|
110
|
+
* @param {Buffer} a
|
|
111
|
+
* @param {Buffer} b
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
114
|
+
wilcocrypt._.constantTimeEqual = function (a, b) {
|
|
115
|
+
if (a.length !== b.length) return false;
|
|
116
|
+
|
|
117
|
+
let result = 0;
|
|
118
|
+
for (let i = 0; i < a.length; i++) {
|
|
119
|
+
result |= a[i] ^ b[i];
|
|
120
|
+
}
|
|
121
|
+
return result === 0;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/* =========================
|
|
125
|
+
Crypto layer (internal)
|
|
126
|
+
========================= */
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Encrypts raw data using AES-256-GCM.
|
|
130
|
+
*
|
|
131
|
+
* @param {Buffer} plainData
|
|
132
|
+
* @param {Buffer} key
|
|
133
|
+
* @param {Buffer} iv
|
|
134
|
+
* @returns {{ciphertext: Buffer, authTag: Buffer}}
|
|
135
|
+
*/
|
|
136
|
+
wilcocrypt._.encryptData = function (plainData, key, iv) {
|
|
137
|
+
wilcocrypt._.assertKeyAndIv(key, iv);
|
|
138
|
+
|
|
139
|
+
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
140
|
+
const encrypted = Buffer.concat([
|
|
141
|
+
cipher.update(plainData),
|
|
142
|
+
cipher.final()
|
|
143
|
+
]);
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
ciphertext: encrypted,
|
|
147
|
+
authTag: cipher.getAuthTag()
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Decrypts AES-256-GCM encrypted data.
|
|
153
|
+
*
|
|
154
|
+
* @param {Buffer} cipherBuffer
|
|
155
|
+
* @param {Buffer} authTagBuffer
|
|
156
|
+
* @param {Buffer} key
|
|
157
|
+
* @param {Buffer} iv
|
|
158
|
+
* @returns {Buffer}
|
|
159
|
+
*/
|
|
160
|
+
wilcocrypt._.decryptData = function (cipherBuffer, authTagBuffer, key, iv) {
|
|
161
|
+
wilcocrypt._.assertKeyAndIv(key, iv);
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
165
|
+
decipher.setAuthTag(authTagBuffer);
|
|
166
|
+
|
|
167
|
+
return Buffer.concat([
|
|
168
|
+
decipher.update(cipherBuffer),
|
|
169
|
+
decipher.final()
|
|
170
|
+
]);
|
|
171
|
+
} catch {
|
|
172
|
+
throw new WilcoCryptError(
|
|
173
|
+
'Decryption failed (invalid password, corrupted data, or tampered file)',
|
|
174
|
+
'DECRYPTION_FAILED'
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/* =========================
|
|
180
|
+
Public API
|
|
181
|
+
========================= */
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Encrypts data using password-based AES-256-GCM.
|
|
185
|
+
*
|
|
186
|
+
* Output format:
|
|
187
|
+
* [HEADER (10 bytes)] + [VERSION (dynamic)] + [salt (16)] + [iv (12)] + [ciphertext] + [authTag (16)]
|
|
188
|
+
*
|
|
189
|
+
* @param {Buffer} plaindata - Raw data to encrypt
|
|
190
|
+
* @param {string} password - Password used for key derivation
|
|
191
|
+
* @param {boolean} [gzip=true] - Whether to compress data before encryption
|
|
192
|
+
* @returns {Buffer} Binary-encoded encrypted payload
|
|
193
|
+
* @throws {WilcoCryptError} If password is invalid
|
|
194
|
+
*/
|
|
195
|
+
wilcocrypt.encryptData = function (plaindata, password, gzip = true) {
|
|
196
|
+
wilcocrypt._.assertPassword(password);
|
|
197
|
+
|
|
198
|
+
const gzipData = gzip ? gzipSync(plaindata) : plaindata;
|
|
199
|
+
const iv = randomBytes(12);
|
|
200
|
+
const salt = randomBytes(16);
|
|
201
|
+
|
|
202
|
+
const key = scryptSync(password, salt, 32);
|
|
203
|
+
|
|
204
|
+
const { ciphertext, authTag } = wilcocrypt._.encryptData(gzipData, key, iv);
|
|
205
|
+
const versionBuf = Buffer.from(wilcocrypt._.VERSION);
|
|
206
|
+
|
|
207
|
+
return Buffer.concat([
|
|
208
|
+
wilcocrypt._.HEADER, // 10 bytes
|
|
209
|
+
versionBuf, // dynamic
|
|
210
|
+
salt, // 16 bytes
|
|
211
|
+
iv, // 12 bytes
|
|
212
|
+
ciphertext, // variable
|
|
213
|
+
authTag // 16 bytes (at the end for streaming compatibility)
|
|
214
|
+
]);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Decrypts encrypted data using password-based AES-256-GCM.
|
|
219
|
+
*
|
|
220
|
+
* Validates internal header and version, then extracts:
|
|
221
|
+
* salt, iv, authTag and ciphertext from the binary payload.
|
|
222
|
+
*
|
|
223
|
+
* @param {Buffer} encryptedBuffer - Binary-encoded encrypted payload
|
|
224
|
+
* @param {string} password - Password used for decryption
|
|
225
|
+
* @param {boolean} [gzip=true] - Whether to decompress after decryption
|
|
226
|
+
* @returns {Buffer} Decrypted raw data
|
|
227
|
+
* @throws {WilcoCryptError} On invalid header, version mismatch, wrong password, or corrupted data
|
|
228
|
+
*/
|
|
229
|
+
wilcocrypt.decryptData = function (encryptedBuffer, password, gzip = true) {
|
|
230
|
+
wilcocrypt._.assertPassword(password);
|
|
231
|
+
|
|
232
|
+
const versionBuf = Buffer.from(wilcocrypt._.VERSION);
|
|
233
|
+
let offset = 0;
|
|
234
|
+
|
|
235
|
+
const fileHeader = encryptedBuffer.subarray(offset, offset += wilcocrypt._.HEADER.length);
|
|
236
|
+
if (!fileHeader.equals(wilcocrypt._.HEADER)) {
|
|
237
|
+
throw new WilcoCryptError('Invalid WilcoCrypt header', 'INVALID_HEADER');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const fileVersion = encryptedBuffer.subarray(offset, offset += versionBuf.length);
|
|
241
|
+
if (!fileVersion.equals(versionBuf)) {
|
|
242
|
+
throw new WilcoCryptError('Version mismatch', 'VERSION_MISMATCH');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const salt = encryptedBuffer.subarray(offset, offset += 16);
|
|
246
|
+
const iv = encryptedBuffer.subarray(offset, offset += 12);
|
|
247
|
+
|
|
248
|
+
// authTag are the last 16 bytes; ciphertext is everything in between
|
|
249
|
+
const authTag = encryptedBuffer.subarray(encryptedBuffer.length - 16);
|
|
250
|
+
const ciphertext = encryptedBuffer.subarray(offset, encryptedBuffer.length - 16);
|
|
251
|
+
|
|
252
|
+
const key = scryptSync(password, salt, 32);
|
|
253
|
+
|
|
254
|
+
const decrypted = wilcocrypt._.decryptData(ciphertext, authTag, key, iv);
|
|
255
|
+
|
|
256
|
+
return gzip ? gunzipSync(decrypted) : decrypted;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Encrypts a file and writes the result to `<filePath>.enc`.
|
|
261
|
+
*
|
|
262
|
+
* @param {string} filePath - Path to the file to encrypt
|
|
263
|
+
* @param {string} password - Password used for encryption
|
|
264
|
+
* @param {boolean} [gzip=true] - Whether to compress before encryption
|
|
265
|
+
* @returns {void}
|
|
266
|
+
* @throws {WilcoCryptError} If password is invalid
|
|
267
|
+
*/
|
|
268
|
+
wilcocrypt.encryptFile = function (filePath, password, gzip = true) {
|
|
269
|
+
const fileData = readFileSync(filePath);
|
|
270
|
+
const encryptedData = wilcocrypt.encryptData(fileData, password, gzip);
|
|
271
|
+
writeFileSync(`${filePath}.enc`, encryptedData);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Decrypts an encrypted `.enc` file.
|
|
276
|
+
*
|
|
277
|
+
* If `outputPath` is provided, the decrypted data is written to that file
|
|
278
|
+
* and `undefined` is returned. Otherwise the decrypted Buffer is returned.
|
|
279
|
+
*
|
|
280
|
+
* @param {string} filePath - Path to the `.enc` file
|
|
281
|
+
* @param {string} password - Password used for decryption
|
|
282
|
+
* @param {string|boolean} [outputPath] - Optional path to write decrypted output to.
|
|
283
|
+
* If omitted (or `true`/`false`), the function returns the decrypted Buffer instead.
|
|
284
|
+
* @param {boolean} [gzip=true] - Whether to decompress after decryption
|
|
285
|
+
* @returns {Buffer|undefined} Decrypted file contents, or undefined if outputPath was given
|
|
286
|
+
* @throws {WilcoCryptError} If file extension is invalid or decryption fails
|
|
287
|
+
*/
|
|
288
|
+
wilcocrypt.decryptFile = function (filePath, password, outputPath, gzip = true) {
|
|
289
|
+
// Support legacy 3-argument form: decryptFile(filePath, password, gzip?)
|
|
290
|
+
if (typeof outputPath === 'boolean') {
|
|
291
|
+
gzip = outputPath;
|
|
292
|
+
outputPath = undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!filePath.endsWith('.enc')) {
|
|
296
|
+
throw new WilcoCryptError(
|
|
297
|
+
'Invalid file extension (expected .enc)',
|
|
298
|
+
'INVALID_FILE_EXTENSION'
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const encryptedData = readFileSync(filePath);
|
|
303
|
+
const decrypted = wilcocrypt.decryptData(encryptedData, password, gzip);
|
|
304
|
+
|
|
305
|
+
if (outputPath) {
|
|
306
|
+
writeFileSync(outputPath, decrypted);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return decrypted;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Encrypts a file using streams and writes the result to `outputPath`.
|
|
315
|
+
* Memory-efficient alternative to `encryptFile` for large files.
|
|
316
|
+
*
|
|
317
|
+
* Output format:
|
|
318
|
+
* [HEADER] + [VERSION] + [salt (16)] + [iv (12)] + [ciphertext] + [authTag (16)]
|
|
319
|
+
*
|
|
320
|
+
* @param {string} inputPath - Path to the file to encrypt
|
|
321
|
+
* @param {string} outputPath - Path to write the encrypted output to
|
|
322
|
+
* @param {string} password - Password used for key derivation
|
|
323
|
+
* @param {boolean} [gzip=true] - Whether to compress data before encryption
|
|
324
|
+
* @returns {Promise<void>}
|
|
325
|
+
* @throws {WilcoCryptError} If password is invalid
|
|
326
|
+
*/
|
|
327
|
+
wilcocrypt.encryptFileStream = async function (inputPath, outputPath, password, gzip = true) {
|
|
328
|
+
wilcocrypt._.assertPassword(password);
|
|
329
|
+
|
|
330
|
+
const salt = randomBytes(16);
|
|
331
|
+
const iv = randomBytes(12);
|
|
332
|
+
const key = scryptSync(password, salt, 32);
|
|
333
|
+
const versionBuf = Buffer.from(wilcocrypt._.VERSION);
|
|
334
|
+
|
|
335
|
+
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
336
|
+
const writeStream = createWriteStream(outputPath);
|
|
337
|
+
|
|
338
|
+
writeStream.write(wilcocrypt._.HEADER);
|
|
339
|
+
writeStream.write(versionBuf);
|
|
340
|
+
writeStream.write(salt);
|
|
341
|
+
writeStream.write(iv);
|
|
342
|
+
|
|
343
|
+
const pipelineSteps = [createReadStream(inputPath)];
|
|
344
|
+
if (gzip) pipelineSteps.push(createGzip());
|
|
345
|
+
pipelineSteps.push(cipher);
|
|
346
|
+
pipelineSteps.push(writeStream);
|
|
347
|
+
|
|
348
|
+
// end: false so we can still append the authTag after the pipeline finishes
|
|
349
|
+
await pipeline(...pipelineSteps, { end: false });
|
|
350
|
+
writeStream.end(cipher.getAuthTag());
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Decrypts an encrypted `.enc` file using streams.
|
|
355
|
+
* Memory-efficient alternative to `decryptFile` for large files.
|
|
356
|
+
* Cleans up the output file automatically if decryption or integrity check fails.
|
|
357
|
+
*
|
|
358
|
+
* @param {string} inputPath - Path to the encrypted `.enc` file
|
|
359
|
+
* @param {string} outputPath - Path to write the decrypted output to
|
|
360
|
+
* @param {string} password - Password used for decryption
|
|
361
|
+
* @param {boolean} [gzip=true] - Whether to decompress after decryption
|
|
362
|
+
* @returns {Promise<void>}
|
|
363
|
+
* @throws {WilcoCryptError} On invalid header, version mismatch, or decryption/integrity failure
|
|
364
|
+
*/
|
|
365
|
+
wilcocrypt.decryptFileStream = async function (inputPath, outputPath, password, gzip = true) {
|
|
366
|
+
wilcocrypt._.assertPassword(password);
|
|
367
|
+
|
|
368
|
+
const handle = await fsPromises.open(inputPath, 'r');
|
|
369
|
+
const versionBuf = Buffer.from(wilcocrypt._.VERSION);
|
|
370
|
+
|
|
371
|
+
const headLen = wilcocrypt._.HEADER.length;
|
|
372
|
+
const verLen = versionBuf.length;
|
|
373
|
+
|
|
374
|
+
const headerCheck = Buffer.alloc(headLen);
|
|
375
|
+
const versionCheck = Buffer.alloc(verLen);
|
|
376
|
+
const salt = Buffer.alloc(16);
|
|
377
|
+
const iv = Buffer.alloc(12);
|
|
378
|
+
|
|
379
|
+
let currentPos = 0;
|
|
380
|
+
await handle.read(headerCheck, 0, headLen, currentPos); currentPos += headLen;
|
|
381
|
+
await handle.read(versionCheck, 0, verLen, currentPos); currentPos += verLen;
|
|
382
|
+
await handle.read(salt, 0, 16, currentPos); currentPos += 16;
|
|
383
|
+
await handle.read(iv, 0, 12, currentPos); currentPos += 12;
|
|
384
|
+
|
|
385
|
+
if (!headerCheck.equals(wilcocrypt._.HEADER)) {
|
|
386
|
+
await handle.close();
|
|
387
|
+
throw new WilcoCryptError('Invalid WilcoCrypt header', 'INVALID_HEADER');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (!versionCheck.equals(versionBuf)) {
|
|
391
|
+
await handle.close();
|
|
392
|
+
throw new WilcoCryptError('Version mismatch', 'VERSION_MISMATCH');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const stats = await handle.stat();
|
|
396
|
+
const authTag = Buffer.alloc(16);
|
|
397
|
+
await handle.read(authTag, 0, 16, stats.size - 16);
|
|
398
|
+
|
|
399
|
+
const key = scryptSync(password, salt, 32);
|
|
400
|
+
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
401
|
+
decipher.setAuthTag(authTag);
|
|
402
|
+
|
|
403
|
+
const pipelineSteps = [createReadStream(inputPath, { start: currentPos, end: stats.size - 17 })];
|
|
404
|
+
pipelineSteps.push(decipher);
|
|
405
|
+
if (gzip) pipelineSteps.push(createGunzip());
|
|
406
|
+
pipelineSteps.push(createWriteStream(outputPath));
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
await pipeline(...pipelineSteps);
|
|
410
|
+
} catch {
|
|
411
|
+
await handle.close();
|
|
412
|
+
await fsPromises.unlink(outputPath);
|
|
413
|
+
throw new WilcoCryptError(
|
|
414
|
+
'Decryption failed (invalid password, corrupted data, or tampered file)',
|
|
415
|
+
'DECRYPTION_FAILED'
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
await handle.close();
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
export default wilcocrypt;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Custom error class for all WilcoCrypt-specific errors.
|
|
5
|
+
*/
|
|
6
|
+
export class WilcoCryptError extends Error {
|
|
7
|
+
code: string;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param message Human-readable error message
|
|
11
|
+
* @param code Machine-readable error code (default: WILCOCRYPT_ERROR)
|
|
12
|
+
*/
|
|
13
|
+
constructor(message: string, code?: string);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Internal helper namespace used by WilcoCrypt.
|
|
18
|
+
*/
|
|
19
|
+
export interface InternalNamespace {
|
|
20
|
+
/**
|
|
21
|
+
* WilcoCrypt version (must match during decryption).
|
|
22
|
+
*/
|
|
23
|
+
VERSION: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Minimum allowed password length.
|
|
27
|
+
*/
|
|
28
|
+
MIN_PASSWORD_LENGTH: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Internal header used to identify valid WilcoCrypt payloads.
|
|
32
|
+
*/
|
|
33
|
+
HEADER: Buffer;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Internal error class used by WilcoCrypt.
|
|
37
|
+
*/
|
|
38
|
+
WilcoCryptError: typeof WilcoCryptError;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Validates AES-256-GCM key and IV.
|
|
42
|
+
*/
|
|
43
|
+
assertKeyAndIv(key: Buffer, iv: Buffer): void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Validates password strength.
|
|
47
|
+
*/
|
|
48
|
+
assertPassword(password: string): void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Constant-time buffer comparison.
|
|
52
|
+
*/
|
|
53
|
+
constantTimeEqual(a: Buffer, b: Buffer): boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Encrypts raw data using AES-256-GCM.
|
|
57
|
+
*/
|
|
58
|
+
encryptData(
|
|
59
|
+
plainData: Buffer,
|
|
60
|
+
key: Buffer,
|
|
61
|
+
iv: Buffer
|
|
62
|
+
): {
|
|
63
|
+
ciphertext: Buffer;
|
|
64
|
+
authTag: Buffer;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Decrypts AES-256-GCM encrypted data.
|
|
69
|
+
*/
|
|
70
|
+
decryptData(
|
|
71
|
+
cipherBuffer: Buffer,
|
|
72
|
+
authTagBuffer: Buffer,
|
|
73
|
+
key: Buffer,
|
|
74
|
+
iv: Buffer
|
|
75
|
+
): Buffer;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Main WilcoCrypt API.
|
|
80
|
+
*/
|
|
81
|
+
export interface WilcoCrypt {
|
|
82
|
+
_: InternalNamespace;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Encrypts data using password-based AES-256-GCM.
|
|
86
|
+
*
|
|
87
|
+
* Output format:
|
|
88
|
+
* [HEADER (10 bytes)] + [VERSION (dynamic)] + [salt (16)] + [iv (12)] + [ciphertext] + [authTag (16)]
|
|
89
|
+
*
|
|
90
|
+
* @param plaindata Raw data to encrypt
|
|
91
|
+
* @param password Password used for key derivation
|
|
92
|
+
* @param gzip Whether to compress data before encryption (default: true)
|
|
93
|
+
* @returns Binary-encoded encrypted payload
|
|
94
|
+
*/
|
|
95
|
+
encryptData(
|
|
96
|
+
plaindata: Buffer,
|
|
97
|
+
password: string,
|
|
98
|
+
gzip?: boolean
|
|
99
|
+
): Buffer;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Decrypts encrypted data using password-based AES-256-GCM.
|
|
103
|
+
*
|
|
104
|
+
* Validates internal header and version, then extracts:
|
|
105
|
+
* salt, iv, authTag and ciphertext from the binary payload.
|
|
106
|
+
*
|
|
107
|
+
* @param encryptedData Binary-encoded encrypted payload
|
|
108
|
+
* @param password Password used for decryption
|
|
109
|
+
* @param gzip Whether to decompress after decryption (default: true)
|
|
110
|
+
* @returns Decrypted raw data
|
|
111
|
+
*
|
|
112
|
+
* @throws WilcoCryptError on:
|
|
113
|
+
* - invalid header
|
|
114
|
+
* - version mismatch
|
|
115
|
+
* - wrong password
|
|
116
|
+
* - corrupted data
|
|
117
|
+
*/
|
|
118
|
+
decryptData(
|
|
119
|
+
encryptedData: Buffer,
|
|
120
|
+
password: string,
|
|
121
|
+
gzip?: boolean
|
|
122
|
+
): Buffer;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Encrypts a file and writes `<filePath>.enc`.
|
|
126
|
+
*/
|
|
127
|
+
encryptFile(
|
|
128
|
+
filePath: string,
|
|
129
|
+
password: string,
|
|
130
|
+
gzip?: boolean
|
|
131
|
+
): void;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Decrypts a `.enc` file.
|
|
135
|
+
*
|
|
136
|
+
* If `outputPath` is provided, the decrypted data is written to that file
|
|
137
|
+
* and `undefined` is returned. Otherwise the decrypted Buffer is returned.
|
|
138
|
+
*/
|
|
139
|
+
decryptFile(filePath: string, password: string, outputPath: string, gzip?: boolean): undefined;
|
|
140
|
+
decryptFile(filePath: string, password: string, gzip?: boolean): Buffer;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Encrypts a file using streams and writes the result to `outputPath`.
|
|
144
|
+
* Memory-efficient alternative to `encryptFile` for large files.
|
|
145
|
+
*
|
|
146
|
+
* @param inputPath Path to the file to encrypt
|
|
147
|
+
* @param outputPath Path to write the encrypted output to
|
|
148
|
+
* @param password Password used for key derivation
|
|
149
|
+
* @param gzip Whether to compress data before encryption (default: true)
|
|
150
|
+
*/
|
|
151
|
+
encryptFileStream(
|
|
152
|
+
inputPath: string,
|
|
153
|
+
outputPath: string,
|
|
154
|
+
password: string,
|
|
155
|
+
gzip?: boolean
|
|
156
|
+
): Promise<void>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Decrypts an encrypted file using streams.
|
|
160
|
+
* Memory-efficient alternative to `decryptFile` for large files.
|
|
161
|
+
* Cleans up the output file automatically if decryption or integrity check fails.
|
|
162
|
+
*
|
|
163
|
+
* @param inputPath Path to the encrypted file
|
|
164
|
+
* @param outputPath Path to write the decrypted output to
|
|
165
|
+
* @param password Password used for decryption
|
|
166
|
+
* @param gzip Whether to decompress after decryption (default: true)
|
|
167
|
+
*
|
|
168
|
+
* @throws WilcoCryptError on:
|
|
169
|
+
* - invalid header
|
|
170
|
+
* - version mismatch
|
|
171
|
+
* - decryption/integrity failure
|
|
172
|
+
*/
|
|
173
|
+
decryptFileStream(
|
|
174
|
+
inputPath: string,
|
|
175
|
+
outputPath: string,
|
|
176
|
+
password: string,
|
|
177
|
+
gzip?: boolean
|
|
178
|
+
): Promise<void>;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* WilcoCrypt main instance.
|
|
183
|
+
*/
|
|
184
|
+
declare const wilcocrypt: WilcoCrypt;
|
|
185
|
+
|
|
186
|
+
export default wilcocrypt;
|