@omnituum/pqc-shared 0.2.6
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/LICENSE +22 -0
- package/README.md +543 -0
- package/dist/crypto/index.cjs +807 -0
- package/dist/crypto/index.d.cts +641 -0
- package/dist/crypto/index.d.ts +641 -0
- package/dist/crypto/index.js +716 -0
- package/dist/decrypt-eSHlbh1j.d.cts +321 -0
- package/dist/decrypt-eSHlbh1j.d.ts +321 -0
- package/dist/fs/index.cjs +1168 -0
- package/dist/fs/index.d.cts +400 -0
- package/dist/fs/index.d.ts +400 -0
- package/dist/fs/index.js +1091 -0
- package/dist/index.cjs +2160 -0
- package/dist/index.d.cts +282 -0
- package/dist/index.d.ts +282 -0
- package/dist/index.js +2031 -0
- package/dist/integrity-CCYjrap3.d.ts +31 -0
- package/dist/integrity-Dx9jukMH.d.cts +31 -0
- package/dist/types-61c7Q9ri.d.ts +134 -0
- package/dist/types-Ch0y-n7K.d.cts +134 -0
- package/dist/utils/index.cjs +129 -0
- package/dist/utils/index.d.cts +49 -0
- package/dist/utils/index.d.ts +49 -0
- package/dist/utils/index.js +114 -0
- package/dist/vault/index.cjs +713 -0
- package/dist/vault/index.d.cts +237 -0
- package/dist/vault/index.d.ts +237 -0
- package/dist/vault/index.js +677 -0
- package/dist/version-BygzPVGs.d.cts +55 -0
- package/dist/version-BygzPVGs.d.ts +55 -0
- package/package.json +86 -0
- package/src/crypto/dilithium.ts +233 -0
- package/src/crypto/hybrid.ts +358 -0
- package/src/crypto/index.ts +181 -0
- package/src/crypto/kyber.ts +199 -0
- package/src/crypto/nacl.ts +204 -0
- package/src/crypto/primitives/blake3.ts +141 -0
- package/src/crypto/primitives/chacha.ts +211 -0
- package/src/crypto/primitives/hkdf.ts +192 -0
- package/src/crypto/primitives/index.ts +54 -0
- package/src/crypto/primitives.ts +144 -0
- package/src/crypto/x25519.ts +134 -0
- package/src/fs/aes.ts +343 -0
- package/src/fs/argon2.ts +184 -0
- package/src/fs/browser.ts +408 -0
- package/src/fs/decrypt.ts +320 -0
- package/src/fs/encrypt.ts +324 -0
- package/src/fs/format.ts +425 -0
- package/src/fs/index.ts +144 -0
- package/src/fs/types.ts +304 -0
- package/src/index.ts +414 -0
- package/src/kdf/index.ts +311 -0
- package/src/runtime/crypto.ts +16 -0
- package/src/security/index.ts +345 -0
- package/src/tunnel/index.ts +39 -0
- package/src/tunnel/session.ts +229 -0
- package/src/tunnel/types.ts +115 -0
- package/src/utils/entropy.ts +128 -0
- package/src/utils/index.ts +25 -0
- package/src/utils/integrity.ts +95 -0
- package/src/vault/decrypt.ts +167 -0
- package/src/vault/encrypt.ts +207 -0
- package/src/vault/index.ts +71 -0
- package/src/vault/manager.ts +327 -0
- package/src/vault/migrate.ts +190 -0
- package/src/vault/types.ts +177 -0
- package/src/version.ts +304 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Omnituum FS - Browser File Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utilities for handling files in the browser environment:
|
|
5
|
+
* - Drag and drop support
|
|
6
|
+
* - File download/upload
|
|
7
|
+
* - Blob/URL handling
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { OQE_MIME_TYPE, OQE_EXTENSION, isOQEFile } from './format';
|
|
11
|
+
import { OQEEncryptResult, OQEDecryptResult } from './types';
|
|
12
|
+
|
|
13
|
+
// Helper to convert Uint8Array to a format compatible with Blob constructor
|
|
14
|
+
function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
15
|
+
// Handle SharedArrayBuffer or offset views by copying
|
|
16
|
+
if (data.buffer instanceof SharedArrayBuffer || data.byteOffset !== 0 || data.byteLength !== data.buffer.byteLength) {
|
|
17
|
+
const copy = new ArrayBuffer(data.byteLength);
|
|
18
|
+
new Uint8Array(copy).set(data);
|
|
19
|
+
return copy;
|
|
20
|
+
}
|
|
21
|
+
return data.buffer as ArrayBuffer;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
25
|
+
// FILE DOWNLOAD
|
|
26
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Download encrypted file to user's device.
|
|
30
|
+
*
|
|
31
|
+
* @param result - Encryption result from encryptFile()
|
|
32
|
+
*/
|
|
33
|
+
export function downloadEncryptedFile(result: OQEEncryptResult): void {
|
|
34
|
+
const blob = new Blob([toArrayBuffer(result.data)], { type: OQE_MIME_TYPE });
|
|
35
|
+
downloadBlob(blob, result.filename);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Download decrypted file to user's device.
|
|
40
|
+
*
|
|
41
|
+
* @param result - Decryption result from decryptFile()
|
|
42
|
+
*/
|
|
43
|
+
export function downloadDecryptedFile(result: OQEDecryptResult): void {
|
|
44
|
+
const mimeType = result.mimeType || 'application/octet-stream';
|
|
45
|
+
const blob = new Blob([toArrayBuffer(result.data)], { type: mimeType });
|
|
46
|
+
downloadBlob(blob, result.filename);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Download a Blob as a file.
|
|
51
|
+
*/
|
|
52
|
+
export function downloadBlob(blob: Blob, filename: string): void {
|
|
53
|
+
const url = URL.createObjectURL(blob);
|
|
54
|
+
|
|
55
|
+
const link = document.createElement('a');
|
|
56
|
+
link.href = url;
|
|
57
|
+
link.download = filename;
|
|
58
|
+
link.style.display = 'none';
|
|
59
|
+
|
|
60
|
+
document.body.appendChild(link);
|
|
61
|
+
link.click();
|
|
62
|
+
document.body.removeChild(link);
|
|
63
|
+
|
|
64
|
+
// Clean up URL after download starts
|
|
65
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Download bytes as a file.
|
|
70
|
+
*/
|
|
71
|
+
export function downloadBytes(data: Uint8Array, filename: string, mimeType?: string): void {
|
|
72
|
+
const blob = new Blob([toArrayBuffer(data)], { type: mimeType || 'application/octet-stream' });
|
|
73
|
+
downloadBlob(blob, filename);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
77
|
+
// FILE READING
|
|
78
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Read a File object as Uint8Array.
|
|
82
|
+
*/
|
|
83
|
+
export async function readFile(file: File): Promise<Uint8Array> {
|
|
84
|
+
const buffer = await file.arrayBuffer();
|
|
85
|
+
return new Uint8Array(buffer);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Read a File object as text.
|
|
90
|
+
*/
|
|
91
|
+
export async function readFileAsText(file: File): Promise<string> {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
const reader = new FileReader();
|
|
94
|
+
reader.onload = () => resolve(reader.result as string);
|
|
95
|
+
reader.onerror = () => reject(reader.error);
|
|
96
|
+
reader.readAsText(file);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Read a File object as Data URL.
|
|
102
|
+
*/
|
|
103
|
+
export async function readFileAsDataURL(file: File): Promise<string> {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
const reader = new FileReader();
|
|
106
|
+
reader.onload = () => resolve(reader.result as string);
|
|
107
|
+
reader.onerror = () => reject(reader.error);
|
|
108
|
+
reader.readAsDataURL(file);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
113
|
+
// DRAG AND DROP
|
|
114
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
115
|
+
|
|
116
|
+
export interface DropZoneOptions {
|
|
117
|
+
/** Element to attach drop zone to */
|
|
118
|
+
element: HTMLElement;
|
|
119
|
+
/** Called when valid files are dropped */
|
|
120
|
+
onDrop: (files: File[]) => void;
|
|
121
|
+
/** Called when drag enters */
|
|
122
|
+
onDragEnter?: () => void;
|
|
123
|
+
/** Called when drag leaves */
|
|
124
|
+
onDragLeave?: () => void;
|
|
125
|
+
/** Filter for accepted file types (e.g., ['image/*', '.pdf']) */
|
|
126
|
+
accept?: string[];
|
|
127
|
+
/** Allow multiple files */
|
|
128
|
+
multiple?: boolean;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Create a drop zone for file drag and drop.
|
|
133
|
+
* Returns a cleanup function to remove listeners.
|
|
134
|
+
*/
|
|
135
|
+
export function createDropZone(options: DropZoneOptions): () => void {
|
|
136
|
+
const { element, onDrop, onDragEnter, onDragLeave, accept, multiple = true } = options;
|
|
137
|
+
|
|
138
|
+
let dragCounter = 0;
|
|
139
|
+
|
|
140
|
+
const handleDragEnter = (e: DragEvent) => {
|
|
141
|
+
e.preventDefault();
|
|
142
|
+
e.stopPropagation();
|
|
143
|
+
dragCounter++;
|
|
144
|
+
if (dragCounter === 1) {
|
|
145
|
+
onDragEnter?.();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const handleDragLeave = (e: DragEvent) => {
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
dragCounter--;
|
|
153
|
+
if (dragCounter === 0) {
|
|
154
|
+
onDragLeave?.();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const handleDragOver = (e: DragEvent) => {
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
e.stopPropagation();
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const handleDrop = (e: DragEvent) => {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
e.stopPropagation();
|
|
166
|
+
dragCounter = 0;
|
|
167
|
+
onDragLeave?.();
|
|
168
|
+
|
|
169
|
+
const files = Array.from(e.dataTransfer?.files || []);
|
|
170
|
+
|
|
171
|
+
// Filter by accept types if specified
|
|
172
|
+
let filteredFiles = files;
|
|
173
|
+
if (accept && accept.length > 0) {
|
|
174
|
+
filteredFiles = files.filter((file) => {
|
|
175
|
+
return accept.some((pattern) => {
|
|
176
|
+
if (pattern.startsWith('.')) {
|
|
177
|
+
return file.name.toLowerCase().endsWith(pattern.toLowerCase());
|
|
178
|
+
}
|
|
179
|
+
if (pattern.endsWith('/*')) {
|
|
180
|
+
const type = pattern.slice(0, -2);
|
|
181
|
+
return file.type.startsWith(type);
|
|
182
|
+
}
|
|
183
|
+
return file.type === pattern;
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Limit to single file if not multiple
|
|
189
|
+
if (!multiple && filteredFiles.length > 1) {
|
|
190
|
+
filteredFiles = [filteredFiles[0]];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (filteredFiles.length > 0) {
|
|
194
|
+
onDrop(filteredFiles);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
element.addEventListener('dragenter', handleDragEnter);
|
|
199
|
+
element.addEventListener('dragleave', handleDragLeave);
|
|
200
|
+
element.addEventListener('dragover', handleDragOver);
|
|
201
|
+
element.addEventListener('drop', handleDrop);
|
|
202
|
+
|
|
203
|
+
// Return cleanup function
|
|
204
|
+
return () => {
|
|
205
|
+
element.removeEventListener('dragenter', handleDragEnter);
|
|
206
|
+
element.removeEventListener('dragleave', handleDragLeave);
|
|
207
|
+
element.removeEventListener('dragover', handleDragOver);
|
|
208
|
+
element.removeEventListener('drop', handleDrop);
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
213
|
+
// FILE INPUT
|
|
214
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
215
|
+
|
|
216
|
+
export interface FileInputOptions {
|
|
217
|
+
/** Filter for accepted file types */
|
|
218
|
+
accept?: string[];
|
|
219
|
+
/** Allow multiple files */
|
|
220
|
+
multiple?: boolean;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Open file picker dialog and return selected files.
|
|
225
|
+
*/
|
|
226
|
+
export function openFilePicker(options: FileInputOptions = {}): Promise<File[]> {
|
|
227
|
+
return new Promise((resolve) => {
|
|
228
|
+
const input = document.createElement('input');
|
|
229
|
+
input.type = 'file';
|
|
230
|
+
input.multiple = options.multiple ?? false;
|
|
231
|
+
|
|
232
|
+
if (options.accept && options.accept.length > 0) {
|
|
233
|
+
input.accept = options.accept.join(',');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
input.onchange = () => {
|
|
237
|
+
const files = Array.from(input.files || []);
|
|
238
|
+
resolve(files);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
input.oncancel = () => {
|
|
242
|
+
resolve([]);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
input.click();
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Open file picker for OQE files specifically.
|
|
251
|
+
*/
|
|
252
|
+
export function openOQEFilePicker(multiple = false): Promise<File[]> {
|
|
253
|
+
return openFilePicker({
|
|
254
|
+
accept: [OQE_EXTENSION, OQE_MIME_TYPE],
|
|
255
|
+
multiple,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Open file picker for any file to encrypt.
|
|
261
|
+
*/
|
|
262
|
+
export function openFileToEncrypt(multiple = false): Promise<File[]> {
|
|
263
|
+
return openFilePicker({ multiple });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
267
|
+
// BLOB UTILITIES
|
|
268
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Create a Blob from encryption result.
|
|
272
|
+
*/
|
|
273
|
+
export function encryptResultToBlob(result: OQEEncryptResult): Blob {
|
|
274
|
+
return new Blob([toArrayBuffer(result.data)], { type: OQE_MIME_TYPE });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Create a Blob from decryption result.
|
|
279
|
+
*/
|
|
280
|
+
export function decryptResultToBlob(result: OQEDecryptResult): Blob {
|
|
281
|
+
const mimeType = result.mimeType || 'application/octet-stream';
|
|
282
|
+
return new Blob([toArrayBuffer(result.data)], { type: mimeType });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Create an object URL for a Blob.
|
|
287
|
+
* Remember to call URL.revokeObjectURL() when done.
|
|
288
|
+
*/
|
|
289
|
+
export function createObjectURL(blob: Blob): string {
|
|
290
|
+
return URL.createObjectURL(blob);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Create a Data URL from bytes.
|
|
295
|
+
*/
|
|
296
|
+
export async function bytesToDataURL(data: Uint8Array, mimeType: string): Promise<string> {
|
|
297
|
+
const blob = new Blob([toArrayBuffer(data)], { type: mimeType });
|
|
298
|
+
return new Promise((resolve, reject) => {
|
|
299
|
+
const reader = new FileReader();
|
|
300
|
+
reader.onload = () => resolve(reader.result as string);
|
|
301
|
+
reader.onerror = () => reject(reader.error);
|
|
302
|
+
reader.readAsDataURL(blob);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
307
|
+
// FILE INFO
|
|
308
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
309
|
+
|
|
310
|
+
export interface FileInfo {
|
|
311
|
+
/** Filename */
|
|
312
|
+
name: string;
|
|
313
|
+
/** File size in bytes */
|
|
314
|
+
size: number;
|
|
315
|
+
/** MIME type */
|
|
316
|
+
type: string;
|
|
317
|
+
/** Last modified timestamp */
|
|
318
|
+
lastModified: number;
|
|
319
|
+
/** Is this an OQE file? */
|
|
320
|
+
isOQE: boolean;
|
|
321
|
+
/** Human-readable size */
|
|
322
|
+
sizeFormatted: string;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Get information about a file.
|
|
327
|
+
*/
|
|
328
|
+
export function getFileInfo(file: File): FileInfo {
|
|
329
|
+
return {
|
|
330
|
+
name: file.name,
|
|
331
|
+
size: file.size,
|
|
332
|
+
type: file.type || 'application/octet-stream',
|
|
333
|
+
lastModified: file.lastModified,
|
|
334
|
+
isOQE: isOQEFile(file.name),
|
|
335
|
+
sizeFormatted: formatFileSize(file.size),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Format file size for display.
|
|
341
|
+
*/
|
|
342
|
+
export function formatFileSize(bytes: number): string {
|
|
343
|
+
if (bytes === 0) return '0 B';
|
|
344
|
+
|
|
345
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
346
|
+
const k = 1024;
|
|
347
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
348
|
+
|
|
349
|
+
return `${(bytes / Math.pow(k, i)).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
353
|
+
// CLIPBOARD
|
|
354
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Copy text to clipboard.
|
|
358
|
+
*/
|
|
359
|
+
export async function copyToClipboard(text: string): Promise<boolean> {
|
|
360
|
+
try {
|
|
361
|
+
await navigator.clipboard.writeText(text);
|
|
362
|
+
return true;
|
|
363
|
+
} catch {
|
|
364
|
+
// Fallback for older browsers
|
|
365
|
+
const textarea = document.createElement('textarea');
|
|
366
|
+
textarea.value = text;
|
|
367
|
+
textarea.style.position = 'fixed';
|
|
368
|
+
textarea.style.left = '-9999px';
|
|
369
|
+
document.body.appendChild(textarea);
|
|
370
|
+
textarea.select();
|
|
371
|
+
const success = document.execCommand('copy');
|
|
372
|
+
document.body.removeChild(textarea);
|
|
373
|
+
return success;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
378
|
+
// ENVIRONMENT DETECTION
|
|
379
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Check if running in a browser environment.
|
|
383
|
+
*/
|
|
384
|
+
export function isBrowser(): boolean {
|
|
385
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Check if Web Crypto API is available.
|
|
390
|
+
*/
|
|
391
|
+
export function isWebCryptoAvailable(): boolean {
|
|
392
|
+
return typeof globalThis.crypto !== 'undefined' && typeof globalThis.crypto.subtle !== 'undefined';
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Check if File API is available.
|
|
397
|
+
*/
|
|
398
|
+
export function isFileAPIAvailable(): boolean {
|
|
399
|
+
return typeof File !== 'undefined' && typeof FileReader !== 'undefined';
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Check if drag and drop is supported.
|
|
404
|
+
*/
|
|
405
|
+
export function isDragDropSupported(): boolean {
|
|
406
|
+
const div = document.createElement('div');
|
|
407
|
+
return 'draggable' in div || ('ondragstart' in div && 'ondrop' in div);
|
|
408
|
+
}
|