@time-file/browser-file-crypto 1.0.3 → 1.1.1

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/dist/index.d.ts CHANGED
@@ -43,6 +43,35 @@ export declare const ALGORITHM: "AES-GCM";
43
43
  */
44
44
  export declare const AUTH_TAG_LENGTH = 16;
45
45
 
46
+ /**
47
+ * Auto encryption options for hybrid mode.
48
+ *
49
+ * @description
50
+ * Extended options that enable automatic switching between
51
+ * non-streaming and streaming encryption based on file size.
52
+ *
53
+ * @since 1.1.0
54
+ */
55
+ export declare interface AutoEncryptOptions extends EncryptOptions {
56
+ /**
57
+ * Enable automatic streaming for large files.
58
+ * When true, files larger than streamingThreshold will use streaming encryption.
59
+ * @default false
60
+ */
61
+ autoStreaming?: boolean;
62
+ /**
63
+ * File size threshold in bytes for automatic streaming mode.
64
+ * Files larger than this size will use streaming encryption.
65
+ * @default 104857600 (100MB)
66
+ */
67
+ streamingThreshold?: number;
68
+ /**
69
+ * Chunk size for streaming encryption (only used when streaming is active).
70
+ * @default 65536 (64KB)
71
+ */
72
+ chunkSize?: number;
73
+ }
74
+
46
75
  /**
47
76
  * Computes SHA-256 hash of a keyfile's key data.
48
77
  *
@@ -78,6 +107,73 @@ export declare const AUTH_TAG_LENGTH = 16;
78
107
  */
79
108
  export declare function computeKeyFileHash(keyData: string): Promise<string>;
80
109
 
110
+ /**
111
+ * Creates a streaming decryption TransformStream.
112
+ *
113
+ * @description
114
+ * Creates a TransformStream that decrypts streaming-encrypted data.
115
+ * The stream automatically parses the header and decrypts each chunk
116
+ * using the appropriate key derivation method.
117
+ *
118
+ * @param options - Streaming decryption options
119
+ * @returns A TransformStream that decrypts data
120
+ *
121
+ * @throws {CryptoError} When password is required but not provided (PASSWORD_REQUIRED)
122
+ * @throws {CryptoError} When keyfile is required but not provided (KEYFILE_REQUIRED)
123
+ * @throws {CryptoError} When decryption fails (DECRYPTION_FAILED)
124
+ * @throws {CryptoError} When format is unsupported (UNSUPPORTED_FORMAT)
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const stream = createDecryptStream({
129
+ * password: 'my-secret',
130
+ * onProgress: ({ processedBytes }) => console.log(`${processedBytes} bytes processed`)
131
+ * });
132
+ *
133
+ * await encryptedStream.pipeThrough(stream).pipeTo(outputStream);
134
+ * ```
135
+ *
136
+ * @since 1.1.0
137
+ */
138
+ export declare function createDecryptStream(options: StreamDecryptOptions): TransformStream<Uint8Array, Uint8Array>;
139
+
140
+ /**
141
+ * Creates a streaming encryption TransformStream.
142
+ *
143
+ * @description
144
+ * Creates a TransformStream that encrypts data in chunks using AES-256-GCM.
145
+ * Each chunk is independently encrypted with a unique IV derived from the
146
+ * base IV and chunk index. This allows memory-efficient encryption of
147
+ * arbitrarily large files.
148
+ *
149
+ * @param options - Streaming encryption options
150
+ * @returns Promise resolving to an object containing the TransformStream and header
151
+ *
152
+ * @throws {CryptoError} When neither password nor keyData is provided (PASSWORD_REQUIRED)
153
+ * @throws {CryptoError} When encryption fails (ENCRYPTION_FAILED)
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * const { stream, header } = await createEncryptStream({
158
+ * password: 'my-secret',
159
+ * chunkSize: 1024 * 1024, // 1MB chunks
160
+ * onProgress: ({ processedBytes }) => console.log(`${processedBytes} bytes processed`)
161
+ * });
162
+ *
163
+ * // Write header first, then pipe data through the stream
164
+ * const writer = writableStream.getWriter();
165
+ * await writer.write(header);
166
+ * writer.releaseLock();
167
+ * await readableStream.pipeThrough(stream).pipeTo(writableStream);
168
+ * ```
169
+ *
170
+ * @since 1.1.0
171
+ */
172
+ export declare function createEncryptStream(options: StreamEncryptOptions): Promise<{
173
+ stream: TransformStream<Uint8Array, Uint8Array>;
174
+ header: Uint8Array;
175
+ }>;
176
+
81
177
  /**
82
178
  * Custom error class for crypto operations.
83
179
  *
@@ -197,6 +293,37 @@ export declare type CryptoErrorCode = 'INVALID_INPUT' | 'PASSWORD_REQUIRED' | 'K
197
293
  */
198
294
  export declare function decryptFile(encrypted: Blob | ArrayBuffer, options: DecryptOptions): Promise<Blob>;
199
295
 
296
+ /**
297
+ * Decrypts a streaming-encrypted file.
298
+ *
299
+ * @description
300
+ * Convenience function that decrypts a streaming-encrypted File, Blob,
301
+ * or ReadableStream and returns a ReadableStream of the decrypted data.
302
+ *
303
+ * @param encrypted - The encrypted data (File, Blob, or ReadableStream)
304
+ * @param options - Streaming decryption options
305
+ * @returns A ReadableStream of decrypted data
306
+ *
307
+ * @throws {CryptoError} When password is required but not provided (PASSWORD_REQUIRED)
308
+ * @throws {CryptoError} When keyfile is required but not provided (KEYFILE_REQUIRED)
309
+ * @throws {CryptoError} When decryption fails (DECRYPTION_FAILED)
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * const decryptedStream = decryptFileStream(encryptedBlob, {
314
+ * password: 'my-secret',
315
+ * onProgress: ({ processedBytes }) => console.log(`${processedBytes} bytes decrypted`)
316
+ * });
317
+ *
318
+ * // Convert to Blob
319
+ * const response = new Response(decryptedStream);
320
+ * const decryptedBlob = await response.blob();
321
+ * ```
322
+ *
323
+ * @since 1.1.0
324
+ */
325
+ export declare function decryptFileStream(encrypted: File | Blob | ReadableStream<Uint8Array>, options: StreamDecryptOptions): ReadableStream<Uint8Array>;
326
+
200
327
  /**
201
328
  * Options for file decryption.
202
329
  *
@@ -223,6 +350,12 @@ export declare interface DecryptOptions {
223
350
  onProgress?: ProgressCallback;
224
351
  }
225
352
 
353
+ /**
354
+ * Default chunk size for streaming encryption (64KB).
355
+ * @since 1.1.0
356
+ */
357
+ export declare const DEFAULT_CHUNK_SIZE: number;
358
+
226
359
  /**
227
360
  * Downloads an encrypted file from URL, decrypts it, and saves to disk.
228
361
  *
@@ -267,6 +400,48 @@ export declare interface DecryptOptions {
267
400
  */
268
401
  export declare function downloadAndDecrypt(url: string, options: DownloadDecryptOptions): Promise<void>;
269
402
 
403
+ /**
404
+ * Downloads a streaming-encrypted file from URL, decrypts it, and saves to disk.
405
+ *
406
+ * @description
407
+ * Memory-efficient version of downloadAndDecrypt for streaming-encrypted files.
408
+ * Uses streaming decryption to process data in chunks, suitable for large files.
409
+ *
410
+ * Phases:
411
+ * 1. `downloading` (0-50%): Fetching file from URL
412
+ * 2. `deriving_key` (50-55%): Key derivation (password mode only)
413
+ * 3. `decrypting` (55-95%): Streaming AES-GCM decryption
414
+ * 4. `complete` (100%): File saved
415
+ *
416
+ * Note: This function uses browser APIs (fetch, URL.createObjectURL, document.createElement)
417
+ * and will not work in Node.js environments.
418
+ *
419
+ * @param url - URL of the streaming-encrypted file to download
420
+ * @param options - Options including password/keyData and fileName
421
+ * @returns Promise that resolves when file is saved
422
+ *
423
+ * @throws {CryptoError} When download fails (DOWNLOAD_FAILED)
424
+ * @throws {CryptoError} When decryption fails (see decryptFileStream errors)
425
+ *
426
+ * @example
427
+ * ```typescript
428
+ * await downloadAndDecryptStream('https://example.com/large-file.enc', {
429
+ * password: 'my-secret',
430
+ * fileName: 'large-video.mp4',
431
+ * onProgress: ({ phase, processedBytes, totalBytes }) => {
432
+ * if (totalBytes) {
433
+ * console.log(`${phase}: ${Math.round((processedBytes / totalBytes) * 100)}%`);
434
+ * }
435
+ * }
436
+ * });
437
+ * ```
438
+ *
439
+ * @see {@link downloadAndDecrypt} for non-streaming files
440
+ * @see {@link decryptFileStream} for decryption only
441
+ * @since 1.1.0
442
+ */
443
+ export declare function downloadAndDecryptStream(url: string, options: DownloadDecryptStreamOptions): Promise<void>;
444
+
270
445
  /**
271
446
  * Options for download and decrypt operation.
272
447
  *
@@ -287,6 +462,16 @@ export declare interface DownloadDecryptOptions extends DecryptOptions {
287
462
  fileName: string;
288
463
  }
289
464
 
465
+ /**
466
+ * Options for streaming download and decrypt operation.
467
+ *
468
+ * @since 1.1.0
469
+ */
470
+ export declare interface DownloadDecryptStreamOptions extends StreamDecryptOptions {
471
+ /** File name to use when saving the decrypted file */
472
+ fileName: string;
473
+ }
474
+
290
475
  /**
291
476
  * Downloads a keyfile as a JSON file with customizable extension.
292
477
  *
@@ -365,27 +550,121 @@ export declare function downloadKeyFile(keyData: string, fileName: string, exten
365
550
  */
366
551
  export declare function encryptFile(file: File | Blob | ArrayBuffer, options: EncryptOptions): Promise<Blob>;
367
552
 
553
+ /**
554
+ * Encrypts a file with automatic streaming mode for large files.
555
+ *
556
+ * @description
557
+ * Hybrid encryption function that automatically chooses between
558
+ * non-streaming and streaming encryption based on file size.
559
+ *
560
+ * - Files smaller than threshold: Uses standard encryptFile (faster for small files)
561
+ * - Files larger than threshold: Uses encryptFileStream (memory-efficient for large files)
562
+ *
563
+ * Default threshold is 100MB, configurable via options.
564
+ *
565
+ * @param file - The file to encrypt (File or Blob)
566
+ * @param options - Auto encryption options including threshold settings
567
+ * @returns Promise resolving to encrypted Blob
568
+ *
569
+ * @throws {CryptoError} When neither password nor keyData is provided (PASSWORD_REQUIRED)
570
+ * @throws {CryptoError} When encryption fails (ENCRYPTION_FAILED)
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * // Auto mode with default 100MB threshold
575
+ * const encrypted = await encryptFileAuto(file, {
576
+ * password: 'my-secret',
577
+ * autoStreaming: true,
578
+ * onProgress: ({ phase, progress }) => console.log(`${phase}: ${progress}%`)
579
+ * });
580
+ *
581
+ * // Custom threshold (50MB)
582
+ * const encrypted = await encryptFileAuto(file, {
583
+ * password: 'my-secret',
584
+ * autoStreaming: true,
585
+ * streamingThreshold: 50 * 1024 * 1024,
586
+ * chunkSize: 1024 * 1024 // 1MB chunks for streaming
587
+ * });
588
+ * ```
589
+ *
590
+ * @see {@link encryptFile} for non-streaming encryption
591
+ * @see {@link encryptFileStream} for streaming encryption
592
+ * @since 1.1.0
593
+ */
594
+ export declare function encryptFileAuto(file: File | Blob, options: AutoEncryptOptions): Promise<Blob>;
595
+
596
+ /**
597
+ * Encrypts a file using streaming encryption.
598
+ *
599
+ * @description
600
+ * Convenience function that encrypts a File or Blob using streaming
601
+ * encryption and returns a ReadableStream of the encrypted data.
602
+ * The stream includes the encryption header followed by encrypted chunks.
603
+ *
604
+ * @param file - The file to encrypt
605
+ * @param options - Streaming encryption options
606
+ * @returns Promise resolving to a ReadableStream of encrypted data
607
+ *
608
+ * @throws {CryptoError} When neither password nor keyData is provided (PASSWORD_REQUIRED)
609
+ * @throws {CryptoError} When encryption fails (ENCRYPTION_FAILED)
610
+ *
611
+ * @example
612
+ * ```typescript
613
+ * const encryptedStream = await encryptFileStream(largeFile, {
614
+ * password: 'my-secret',
615
+ * chunkSize: 1024 * 1024, // 1MB chunks
616
+ * onProgress: ({ processedBytes, totalBytes }) => {
617
+ * const percent = Math.round((processedBytes / totalBytes!) * 100);
618
+ * console.log(`${percent}%`);
619
+ * }
620
+ * });
621
+ *
622
+ * // Convert to Blob
623
+ * const response = new Response(encryptedStream);
624
+ * const encryptedBlob = await response.blob();
625
+ * ```
626
+ *
627
+ * @since 1.1.0
628
+ */
629
+ export declare function encryptFileStream(file: File | Blob, options: StreamEncryptOptions): Promise<ReadableStream<Uint8Array>>;
630
+
368
631
  /**
369
632
  * Encryption marker byte for keyfile-based encryption.
370
633
  * Used to identify the encryption method when decrypting.
371
634
  */
372
635
  export declare const ENCRYPTION_MARKER_KEYFILE = 2;
373
636
 
637
+ /**
638
+ * Encryption marker byte for keyfile-based streaming encryption.
639
+ * Used to identify streaming encryption with keyfile.
640
+ * @since 1.1.0
641
+ */
642
+ export declare const ENCRYPTION_MARKER_KEYFILE_STREAM = 18;
643
+
374
644
  /**
375
645
  * Encryption marker byte for password-based encryption.
376
646
  * Used to identify the encryption method when decrypting.
377
647
  */
378
648
  export declare const ENCRYPTION_MARKER_PASSWORD = 1;
379
649
 
650
+ /**
651
+ * Encryption marker byte for password-based streaming encryption.
652
+ * Used to identify streaming encryption with password.
653
+ * @since 1.1.0
654
+ */
655
+ export declare const ENCRYPTION_MARKER_PASSWORD_STREAM = 17;
656
+
380
657
  /**
381
658
  * Encryption type detected from encrypted data.
382
659
  *
383
660
  * @description
384
661
  * - `password`: File was encrypted with a password (marker byte 0x01)
385
662
  * - `keyfile`: File was encrypted with a keyfile (marker byte 0x02)
663
+ * - `password-stream`: File was encrypted with password streaming (marker byte 0x11)
664
+ * - `keyfile-stream`: File was encrypted with keyfile streaming (marker byte 0x12)
386
665
  * - `unknown`: Unrecognized format or not encrypted with this library
387
666
  */
388
- export declare type EncryptionType = 'password' | 'keyfile' | 'unknown';
667
+ export declare type EncryptionType = 'password' | 'keyfile' | 'password-stream' | 'keyfile-stream' | 'unknown';
389
668
 
390
669
  /**
391
670
  * Options for file encryption.
@@ -486,10 +765,13 @@ export declare function generateRandomPassword(length?: number): string;
486
765
  *
487
766
  * @description
488
767
  * Reads the first byte (marker) of the encrypted data to determine
489
- * whether it was encrypted with a password or keyfile.
768
+ * whether it was encrypted with a password or keyfile, and whether
769
+ * it uses streaming or non-streaming encryption.
490
770
  *
491
- * - Marker 0x01: Password-based encryption
492
- * - Marker 0x02: Keyfile-based encryption
771
+ * - Marker 0x01: Password-based encryption (non-streaming)
772
+ * - Marker 0x02: Keyfile-based encryption (non-streaming)
773
+ * - Marker 0x11: Password-based streaming encryption
774
+ * - Marker 0x12: Keyfile-based streaming encryption
493
775
  * - Other: Unknown format
494
776
  *
495
777
  * @param data - The encrypted data (Blob or ArrayBuffer)
@@ -501,10 +783,16 @@ export declare function generateRandomPassword(length?: number): string;
501
783
  *
502
784
  * switch (type) {
503
785
  * case 'password':
504
- * // Show password input
786
+ * // Use decryptFile with password
505
787
  * break;
506
788
  * case 'keyfile':
507
- * // Show keyfile picker
789
+ * // Use decryptFile with keyData
790
+ * break;
791
+ * case 'password-stream':
792
+ * // Use decryptFileStream with password
793
+ * break;
794
+ * case 'keyfile-stream':
795
+ * // Use decryptFileStream with keyData
508
796
  * break;
509
797
  * case 'unknown':
510
798
  * // Show error: not encrypted with this library
@@ -547,7 +835,7 @@ export declare function isCryptoError(error: unknown): error is CryptoError;
547
835
  * @description
548
836
  * Performs a quick check to determine if the data was likely encrypted
549
837
  * with browser-file-crypto. This checks:
550
- * 1. The marker byte is valid (0x01 or 0x02)
838
+ * 1. The marker byte is valid (0x01, 0x02, 0x11, or 0x12)
551
839
  * 2. The data meets minimum size requirements
552
840
  *
553
841
  * Note: This is not a cryptographic verification. It only checks
@@ -572,6 +860,31 @@ export declare function isCryptoError(error: unknown): error is CryptoError;
572
860
  */
573
861
  export declare function isEncryptedFile(data: Blob | ArrayBuffer): Promise<boolean>;
574
862
 
863
+ /**
864
+ * Checks if the encryption type is a streaming format.
865
+ *
866
+ * @description
867
+ * Helper function to determine if the encryption type uses
868
+ * streaming encryption, which requires different decryption methods.
869
+ *
870
+ * @param type - The encryption type
871
+ * @returns true if the type is a streaming format
872
+ *
873
+ * @example
874
+ * ```typescript
875
+ * const type = await getEncryptionType(encryptedBlob);
876
+ *
877
+ * if (isStreamingEncryption(type)) {
878
+ * const decrypted = decryptFileStream(encryptedBlob, { password });
879
+ * } else {
880
+ * const decrypted = await decryptFile(encryptedBlob, { password });
881
+ * }
882
+ * ```
883
+ *
884
+ * @since 1.1.0
885
+ */
886
+ export declare function isStreamingEncryption(type: EncryptionType): boolean;
887
+
575
888
  /**
576
889
  * Initialization vector length in bytes for AES-GCM.
577
890
  * 12 bytes (96 bits) is the recommended IV size for AES-GCM per NIST SP 800-38D.
@@ -723,4 +1036,82 @@ export declare type ProgressPhase = 'deriving_key' | 'encrypting' | 'decrypting'
723
1036
  */
724
1037
  export declare const SALT_LENGTH = 16;
725
1038
 
1039
+ /**
1040
+ * Streaming format version.
1041
+ * @since 1.1.0
1042
+ */
1043
+ export declare const STREAM_FORMAT_VERSION = 1;
1044
+
1045
+ /**
1046
+ * Options for streaming file decryption.
1047
+ *
1048
+ * @since 1.1.0
1049
+ */
1050
+ export declare interface StreamDecryptOptions {
1051
+ /** Password for decryption (required for password-encrypted files) */
1052
+ password?: string;
1053
+ /** Base64-encoded key data (required for keyfile-encrypted files) */
1054
+ keyData?: string;
1055
+ /** Progress callback for UI updates */
1056
+ onProgress?: StreamProgressCallback;
1057
+ }
1058
+
1059
+ /**
1060
+ * Options for streaming file encryption.
1061
+ *
1062
+ * @since 1.1.0
1063
+ */
1064
+ export declare interface StreamEncryptOptions {
1065
+ /** Password for encryption (required if keyData not provided) */
1066
+ password?: string;
1067
+ /** Base64-encoded key data from keyfile (required if password not provided) */
1068
+ keyData?: string;
1069
+ /** Chunk size in bytes (default: 65536 = 64KB) */
1070
+ chunkSize?: number;
1071
+ /** Progress callback for UI updates */
1072
+ onProgress?: StreamProgressCallback;
1073
+ }
1074
+
1075
+ /**
1076
+ * Progress information for streaming encryption/decryption operations.
1077
+ *
1078
+ * @since 1.1.0
1079
+ */
1080
+ export declare interface StreamProgress {
1081
+ /** Current phase of the operation */
1082
+ phase: StreamProgressPhase;
1083
+ /** Number of bytes processed so far */
1084
+ processedBytes: number;
1085
+ /** Total bytes to process (undefined if unknown, e.g., for streams) */
1086
+ totalBytes?: number;
1087
+ /** Number of chunks processed so far */
1088
+ processedChunks: number;
1089
+ /** Progress percentage (0-100), only available when totalBytes is known */
1090
+ progress?: number;
1091
+ }
1092
+
1093
+ /**
1094
+ * Callback function for streaming progress updates.
1095
+ *
1096
+ * @since 1.1.0
1097
+ */
1098
+ export declare type StreamProgressCallback = (progress: StreamProgress) => void;
1099
+
1100
+ /**
1101
+ * Progress phase during streaming encryption/decryption operations.
1102
+ *
1103
+ * @description
1104
+ * - `deriving_key`: PBKDF2 key derivation in progress (password mode)
1105
+ * - `encrypting`: AES-GCM streaming encryption in progress
1106
+ * - `decrypting`: AES-GCM streaming decryption in progress
1107
+ * - `downloading`: File download in progress (downloadAndDecryptStream)
1108
+ * - `complete`: Operation finished successfully
1109
+ *
1110
+ * Note: For backward compatibility, these phases are aligned with
1111
+ * the non-streaming Progress phases.
1112
+ *
1113
+ * @since 1.1.0
1114
+ */
1115
+ export declare type StreamProgressPhase = 'deriving_key' | 'encrypting' | 'decrypting' | 'downloading' | 'complete';
1116
+
726
1117
  export { }