uppy-encrypt 1.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/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.d.mts +57 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +354 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +317 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 0sum Co
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Uppy Encrypt
|
|
2
|
+
|
|
3
|
+
An [Uppy](https://uppy.io/) Plugin to encrypt files on the browser before it's uploaded. Uppy Encrypt also comes with the ability to decrypt browser-side.
|
|
4
|
+
|
|
5
|
+
Uppy Encrypt uses [libsodium.js](https://github.com/jedisct1/libsodium.js) for all the cryptographical magic.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i uppy-encrypt
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Encryption Example
|
|
14
|
+
```javascript
|
|
15
|
+
import { Uppy } from '@uppy/core';
|
|
16
|
+
import UppyEncryptPlugin from 'uppy-encrypt';
|
|
17
|
+
|
|
18
|
+
const uppy = new Uppy();
|
|
19
|
+
uppy.use(UppyEncryptPlugin);
|
|
20
|
+
|
|
21
|
+
// Optional: Set password manually, or disregard and a random password will be auto-generated
|
|
22
|
+
// uppy.setMeta({ password: '$upers3cret!' });
|
|
23
|
+
|
|
24
|
+
uppy.on('complete', async (result) => {
|
|
25
|
+
for (const file of result.successful) {
|
|
26
|
+
const salt = file.meta.encryption.salt; // Salt value used to increase security
|
|
27
|
+
const header = file.meta.encryption.header; // Header encryption data to kick off the decryption process
|
|
28
|
+
const hash = file.meta.encryption.hash; // Secure 1-way hash of the password
|
|
29
|
+
const meta = file.meta.encryption.meta; // Encrypted file meta data (file name, type)
|
|
30
|
+
// ^ These are all safe to store in a database
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Decryption Example
|
|
36
|
+
```javascript
|
|
37
|
+
import { UppyDecrypt, uppyEncryptReady } from 'uppy-encrypt';
|
|
38
|
+
|
|
39
|
+
// Use the values generated from the encryption process
|
|
40
|
+
// Usually, these would be stored/retrieved from a database
|
|
41
|
+
const decrypt = async (hash, password, salt, header, meta, encryptedFileUrl) => {
|
|
42
|
+
// Ensure required libraries are loaded
|
|
43
|
+
await uppyEncryptReady();
|
|
44
|
+
|
|
45
|
+
// Verify provided password against the stored hash value
|
|
46
|
+
if (!UppyDecrypt.verifyPassword(hash, password)) {
|
|
47
|
+
// Invalid password
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Decrypt Metadata
|
|
52
|
+
const decryptor = new UppyDecrypt(password, salt, header);
|
|
53
|
+
const decryptedMeta = decryptor.getDecryptedMetaData(meta.header, meta.data);
|
|
54
|
+
|
|
55
|
+
// Fetch & Decrypt the encrypted file
|
|
56
|
+
const file = await fetch(encryptedFileUrl);
|
|
57
|
+
const blob = await file.blob();
|
|
58
|
+
const decrypted = await decryptor.decryptFile(blob);
|
|
59
|
+
|
|
60
|
+
// Do something with the decrypted file, like download it
|
|
61
|
+
if (decrypted) {
|
|
62
|
+
const aElement = document.createElement('a');
|
|
63
|
+
aElement.setAttribute('download', decryptedMeta.name);
|
|
64
|
+
const href = URL.createObjectURL(decrypted);
|
|
65
|
+
aElement.href = href;
|
|
66
|
+
aElement.setAttribute('target', '_blank');
|
|
67
|
+
aElement.click();
|
|
68
|
+
URL.revokeObjectURL(href);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Uppy, UppyFile, BasePlugin, DefaultPluginOptions } from '@uppy/core';
|
|
2
|
+
|
|
3
|
+
declare class UppyEncrypt {
|
|
4
|
+
private uppy;
|
|
5
|
+
private password;
|
|
6
|
+
private salt;
|
|
7
|
+
private key;
|
|
8
|
+
private state;
|
|
9
|
+
private header;
|
|
10
|
+
private file;
|
|
11
|
+
private stream;
|
|
12
|
+
private streamController;
|
|
13
|
+
private streamCanceled;
|
|
14
|
+
private index;
|
|
15
|
+
constructor(uppy: Uppy, file: UppyFile<Record<string, unknown>, Record<string, unknown>>, password: string);
|
|
16
|
+
static generatePassword(): string;
|
|
17
|
+
encryptFile(): Promise<boolean>;
|
|
18
|
+
getEncryptedFile(): Promise<Blob>;
|
|
19
|
+
getEncryptMetaData(): {
|
|
20
|
+
header: string;
|
|
21
|
+
data: string;
|
|
22
|
+
};
|
|
23
|
+
getPasswordHash(): string;
|
|
24
|
+
getHeader(): string;
|
|
25
|
+
getSalt(): string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface DecryptedMetaData {
|
|
29
|
+
name: string;
|
|
30
|
+
type?: string;
|
|
31
|
+
}
|
|
32
|
+
declare class UppyDecrypt {
|
|
33
|
+
private key;
|
|
34
|
+
private state;
|
|
35
|
+
private stream;
|
|
36
|
+
private streamController;
|
|
37
|
+
private contentType;
|
|
38
|
+
private index;
|
|
39
|
+
constructor(password: string, salt: string, header: string);
|
|
40
|
+
static verifyPassword(hash: string, password: string): boolean;
|
|
41
|
+
decryptFile(file: Blob): Promise<Blob>;
|
|
42
|
+
getDecryptedMetaData(header: string, meta: string): DecryptedMetaData;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface UppyEncryptPluginOptions extends DefaultPluginOptions {
|
|
46
|
+
password: string | null;
|
|
47
|
+
}
|
|
48
|
+
declare const uppyEncryptReady: () => Promise<void>;
|
|
49
|
+
declare class UppyEncryptPlugin extends BasePlugin {
|
|
50
|
+
opts: UppyEncryptPluginOptions;
|
|
51
|
+
constructor(uppy: Uppy, opts?: UppyEncryptPluginOptions | undefined);
|
|
52
|
+
encryptFiles(fileIds: string[]): Promise<void>;
|
|
53
|
+
install(): void;
|
|
54
|
+
uninstall(): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { type DecryptedMetaData, UppyDecrypt, UppyEncrypt, UppyEncryptPlugin, uppyEncryptReady };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Uppy, UppyFile, BasePlugin, DefaultPluginOptions } from '@uppy/core';
|
|
2
|
+
|
|
3
|
+
declare class UppyEncrypt {
|
|
4
|
+
private uppy;
|
|
5
|
+
private password;
|
|
6
|
+
private salt;
|
|
7
|
+
private key;
|
|
8
|
+
private state;
|
|
9
|
+
private header;
|
|
10
|
+
private file;
|
|
11
|
+
private stream;
|
|
12
|
+
private streamController;
|
|
13
|
+
private streamCanceled;
|
|
14
|
+
private index;
|
|
15
|
+
constructor(uppy: Uppy, file: UppyFile<Record<string, unknown>, Record<string, unknown>>, password: string);
|
|
16
|
+
static generatePassword(): string;
|
|
17
|
+
encryptFile(): Promise<boolean>;
|
|
18
|
+
getEncryptedFile(): Promise<Blob>;
|
|
19
|
+
getEncryptMetaData(): {
|
|
20
|
+
header: string;
|
|
21
|
+
data: string;
|
|
22
|
+
};
|
|
23
|
+
getPasswordHash(): string;
|
|
24
|
+
getHeader(): string;
|
|
25
|
+
getSalt(): string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface DecryptedMetaData {
|
|
29
|
+
name: string;
|
|
30
|
+
type?: string;
|
|
31
|
+
}
|
|
32
|
+
declare class UppyDecrypt {
|
|
33
|
+
private key;
|
|
34
|
+
private state;
|
|
35
|
+
private stream;
|
|
36
|
+
private streamController;
|
|
37
|
+
private contentType;
|
|
38
|
+
private index;
|
|
39
|
+
constructor(password: string, salt: string, header: string);
|
|
40
|
+
static verifyPassword(hash: string, password: string): boolean;
|
|
41
|
+
decryptFile(file: Blob): Promise<Blob>;
|
|
42
|
+
getDecryptedMetaData(header: string, meta: string): DecryptedMetaData;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface UppyEncryptPluginOptions extends DefaultPluginOptions {
|
|
46
|
+
password: string | null;
|
|
47
|
+
}
|
|
48
|
+
declare const uppyEncryptReady: () => Promise<void>;
|
|
49
|
+
declare class UppyEncryptPlugin extends BasePlugin {
|
|
50
|
+
opts: UppyEncryptPluginOptions;
|
|
51
|
+
constructor(uppy: Uppy, opts?: UppyEncryptPluginOptions | undefined);
|
|
52
|
+
encryptFiles(fileIds: string[]): Promise<void>;
|
|
53
|
+
install(): void;
|
|
54
|
+
uninstall(): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { type DecryptedMetaData, UppyDecrypt, UppyEncrypt, UppyEncryptPlugin, uppyEncryptReady };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
var __export = (target, all) => {
|
|
23
|
+
for (var name in all)
|
|
24
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
25
|
+
};
|
|
26
|
+
var __copyProps = (to, from, except, desc) => {
|
|
27
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
28
|
+
for (let key of __getOwnPropNames(from))
|
|
29
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
30
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
31
|
+
}
|
|
32
|
+
return to;
|
|
33
|
+
};
|
|
34
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
35
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
36
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
37
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
38
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
39
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
40
|
+
mod
|
|
41
|
+
));
|
|
42
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
43
|
+
var __async = (__this, __arguments, generator) => {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
var fulfilled = (value) => {
|
|
46
|
+
try {
|
|
47
|
+
step(generator.next(value));
|
|
48
|
+
} catch (e) {
|
|
49
|
+
reject(e);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var rejected = (value) => {
|
|
53
|
+
try {
|
|
54
|
+
step(generator.throw(value));
|
|
55
|
+
} catch (e) {
|
|
56
|
+
reject(e);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
60
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/index.ts
|
|
65
|
+
var src_exports = {};
|
|
66
|
+
__export(src_exports, {
|
|
67
|
+
UppyDecrypt: () => UppyDecrypt,
|
|
68
|
+
UppyEncrypt: () => UppyEncrypt,
|
|
69
|
+
UppyEncryptPlugin: () => UppyEncryptPlugin,
|
|
70
|
+
uppyEncryptReady: () => uppyEncryptReady
|
|
71
|
+
});
|
|
72
|
+
module.exports = __toCommonJS(src_exports);
|
|
73
|
+
|
|
74
|
+
// src/UppyEncrypt.ts
|
|
75
|
+
var import_libsodium_wrappers_sumo = __toESM(require("libsodium-wrappers-sumo"));
|
|
76
|
+
|
|
77
|
+
// src/constants.ts
|
|
78
|
+
var CHUNK_SIZE = 64 * 1024 * 1024;
|
|
79
|
+
var SIGNATURE = "uppyencrypt";
|
|
80
|
+
|
|
81
|
+
// src/UppyEncrypt.ts
|
|
82
|
+
var sodium;
|
|
83
|
+
(() => __async(void 0, null, function* () {
|
|
84
|
+
yield import_libsodium_wrappers_sumo.default.ready;
|
|
85
|
+
sodium = import_libsodium_wrappers_sumo.default;
|
|
86
|
+
}))();
|
|
87
|
+
var UppyEncrypt = class {
|
|
88
|
+
constructor(uppy, file, password) {
|
|
89
|
+
this.streamCanceled = false;
|
|
90
|
+
this.index = 0;
|
|
91
|
+
this.uppy = uppy;
|
|
92
|
+
this.file = file;
|
|
93
|
+
this.password = password;
|
|
94
|
+
uppy.on("cancel-all", () => {
|
|
95
|
+
this.streamCanceled = true;
|
|
96
|
+
});
|
|
97
|
+
this.streamController;
|
|
98
|
+
this.stream = new ReadableStream({
|
|
99
|
+
start: (controller) => {
|
|
100
|
+
this.streamController = controller;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
this.salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);
|
|
104
|
+
this.key = sodium.crypto_pwhash(
|
|
105
|
+
sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES,
|
|
106
|
+
password,
|
|
107
|
+
this.salt,
|
|
108
|
+
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
109
|
+
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
110
|
+
sodium.crypto_pwhash_ALG_ARGON2ID13
|
|
111
|
+
);
|
|
112
|
+
const res = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);
|
|
113
|
+
this.state = res.state;
|
|
114
|
+
this.header = res.header;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Helper function that generate a random password
|
|
118
|
+
*/
|
|
119
|
+
static generatePassword() {
|
|
120
|
+
return sodium.to_base64(sodium.randombytes_buf(16), sodium.base64_variants.URLSAFE_NO_PADDING);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Encrypts the file
|
|
124
|
+
*/
|
|
125
|
+
encryptFile() {
|
|
126
|
+
return __async(this, null, function* () {
|
|
127
|
+
if (!this.streamController) {
|
|
128
|
+
throw new Error("Encryption stream does not exist");
|
|
129
|
+
}
|
|
130
|
+
while (this.index < this.file.size) {
|
|
131
|
+
if (this.streamCanceled) {
|
|
132
|
+
yield this.stream.cancel();
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (this.index === 0) {
|
|
136
|
+
this.streamController.enqueue(new Uint8Array(new TextEncoder().encode(SIGNATURE)));
|
|
137
|
+
this.streamController.enqueue(this.salt);
|
|
138
|
+
this.streamController.enqueue(this.header);
|
|
139
|
+
}
|
|
140
|
+
const tag = this.index + CHUNK_SIZE < this.file.size ? sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE : sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL;
|
|
141
|
+
const chunk = yield this.file.data.slice(this.index, this.index + CHUNK_SIZE).arrayBuffer();
|
|
142
|
+
const encryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_push(this.state, new Uint8Array(chunk), null, tag);
|
|
143
|
+
this.streamController.enqueue(new Uint8Array(encryptedChunk));
|
|
144
|
+
this.uppy.emit("preprocess-progress", this.file, {
|
|
145
|
+
mode: "determinate",
|
|
146
|
+
message: `Encrypting ${this.file.name}...`,
|
|
147
|
+
value: this.index / this.file.size
|
|
148
|
+
});
|
|
149
|
+
this.index += CHUNK_SIZE;
|
|
150
|
+
}
|
|
151
|
+
this.uppy.emit("preprocess-progress", this.file, {
|
|
152
|
+
mode: "determinate",
|
|
153
|
+
message: `Encrypting ${this.file.name}...`,
|
|
154
|
+
value: 1
|
|
155
|
+
});
|
|
156
|
+
this.streamController.close();
|
|
157
|
+
return true;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Creates and returns a Blob of the encrypted file
|
|
162
|
+
*/
|
|
163
|
+
getEncryptedFile() {
|
|
164
|
+
return __async(this, null, function* () {
|
|
165
|
+
const response = new Response(this.stream);
|
|
166
|
+
return response.blob();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Returns an encrypted representation of the file's metadata (name, content-type)
|
|
171
|
+
* header: base64-encoded header data
|
|
172
|
+
* meta: Encrypted JSON string of the file's metadata, base64-encoded
|
|
173
|
+
*/
|
|
174
|
+
getEncryptMetaData() {
|
|
175
|
+
const res = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);
|
|
176
|
+
const metaJson = JSON.stringify({ name: this.file.meta.name, type: this.file.meta.type || null });
|
|
177
|
+
const encryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_push(
|
|
178
|
+
res.state,
|
|
179
|
+
new Uint8Array(new TextEncoder().encode(metaJson)),
|
|
180
|
+
null,
|
|
181
|
+
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
|
|
182
|
+
);
|
|
183
|
+
return {
|
|
184
|
+
header: sodium.to_base64(res.header, sodium.base64_variants.URLSAFE_NO_PADDING),
|
|
185
|
+
data: sodium.to_base64(encryptedChunk, sodium.base64_variants.URLSAFE_NO_PADDING)
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Returns a hash of the password base64-encoded
|
|
190
|
+
* This data is safe to store in a database, etc
|
|
191
|
+
*/
|
|
192
|
+
getPasswordHash() {
|
|
193
|
+
return sodium.crypto_pwhash_str(this.password, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Returns the header base64-encoded
|
|
197
|
+
* This data is safe to store in a database, etc
|
|
198
|
+
*/
|
|
199
|
+
getHeader() {
|
|
200
|
+
return sodium.to_base64(this.header, sodium.base64_variants.URLSAFE_NO_PADDING);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Returns the salt base64-encoded
|
|
204
|
+
* This data is safe to store in a database, etc
|
|
205
|
+
*/
|
|
206
|
+
getSalt() {
|
|
207
|
+
return sodium.to_base64(this.salt, sodium.base64_variants.URLSAFE_NO_PADDING);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/UppyDecrypt.ts
|
|
212
|
+
var import_libsodium_wrappers_sumo2 = __toESM(require("libsodium-wrappers-sumo"));
|
|
213
|
+
var sodium2;
|
|
214
|
+
(() => __async(void 0, null, function* () {
|
|
215
|
+
yield import_libsodium_wrappers_sumo2.default.ready;
|
|
216
|
+
sodium2 = import_libsodium_wrappers_sumo2.default;
|
|
217
|
+
}))();
|
|
218
|
+
var UppyDecrypt = class {
|
|
219
|
+
constructor(password, salt, header) {
|
|
220
|
+
this.index = 0;
|
|
221
|
+
const saltUint = sodium2.from_base64(salt, sodium2.base64_variants.URLSAFE_NO_PADDING);
|
|
222
|
+
const headerUint = sodium2.from_base64(header, sodium2.base64_variants.URLSAFE_NO_PADDING);
|
|
223
|
+
this.streamController;
|
|
224
|
+
this.stream = new ReadableStream({
|
|
225
|
+
start: (controller) => {
|
|
226
|
+
this.streamController = controller;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
this.contentType = "";
|
|
230
|
+
this.key = sodium2.crypto_pwhash(
|
|
231
|
+
sodium2.crypto_secretstream_xchacha20poly1305_KEYBYTES,
|
|
232
|
+
password,
|
|
233
|
+
saltUint,
|
|
234
|
+
sodium2.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
235
|
+
sodium2.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
236
|
+
sodium2.crypto_pwhash_ALG_ARGON2ID13
|
|
237
|
+
);
|
|
238
|
+
this.state = sodium2.crypto_secretstream_xchacha20poly1305_init_pull(headerUint, this.key);
|
|
239
|
+
this.index = SIGNATURE.length + saltUint.length + headerUint.length;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Validates that the provided password is correct
|
|
243
|
+
* @param hash The hash value of the password created during UppyEncrypt
|
|
244
|
+
* @param password The user-provided password
|
|
245
|
+
* @returns {bool} true if correct password
|
|
246
|
+
*/
|
|
247
|
+
static verifyPassword(hash, password) {
|
|
248
|
+
return sodium2.crypto_pwhash_str_verify(hash, password);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Decrypts the provided file
|
|
252
|
+
* @param file Blob of encryptyed file
|
|
253
|
+
* @returns Decrypted file as a blob
|
|
254
|
+
*/
|
|
255
|
+
decryptFile(file) {
|
|
256
|
+
return __async(this, null, function* () {
|
|
257
|
+
if (!this.streamController) {
|
|
258
|
+
throw new Error("Encryption stream does not exist");
|
|
259
|
+
}
|
|
260
|
+
while (this.index < file.size) {
|
|
261
|
+
const chunk = yield file.slice(this.index, this.index + CHUNK_SIZE + sodium2.crypto_secretstream_xchacha20poly1305_ABYTES).arrayBuffer();
|
|
262
|
+
const decryptedChunk = sodium2.crypto_secretstream_xchacha20poly1305_pull(this.state, new Uint8Array(chunk));
|
|
263
|
+
this.streamController.enqueue(decryptedChunk.message);
|
|
264
|
+
this.index += CHUNK_SIZE + sodium2.crypto_secretstream_xchacha20poly1305_ABYTES;
|
|
265
|
+
}
|
|
266
|
+
this.streamController.close();
|
|
267
|
+
const response = new Response(this.stream, { headers: { "Content-Type": this.contentType } });
|
|
268
|
+
return response.blob();
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
*
|
|
273
|
+
* @param header Header created during encryption of the meta data
|
|
274
|
+
* @param meta Encrypted meta data string
|
|
275
|
+
* @returns object of the decrypted meta data
|
|
276
|
+
*/
|
|
277
|
+
getDecryptedMetaData(header, meta) {
|
|
278
|
+
const state = sodium2.crypto_secretstream_xchacha20poly1305_init_pull(sodium2.from_base64(header, sodium2.base64_variants.URLSAFE_NO_PADDING), this.key);
|
|
279
|
+
const decryptedChunk = sodium2.crypto_secretstream_xchacha20poly1305_pull(state, sodium2.from_base64(meta, sodium2.base64_variants.URLSAFE_NO_PADDING));
|
|
280
|
+
if (!decryptedChunk)
|
|
281
|
+
throw new Error("Unable to decrypt meta data");
|
|
282
|
+
const decryptedMeta = JSON.parse(new TextDecoder().decode(decryptedChunk.message));
|
|
283
|
+
if (decryptedMeta.type)
|
|
284
|
+
this.contentType = decryptedMeta.type;
|
|
285
|
+
return decryptedMeta;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// src/index.ts
|
|
290
|
+
var import_libsodium_wrappers_sumo3 = __toESM(require("libsodium-wrappers-sumo"));
|
|
291
|
+
var import_core = require("@uppy/core");
|
|
292
|
+
var sodiumIsReady = false;
|
|
293
|
+
var uppyEncryptReady = () => __async(void 0, null, function* () {
|
|
294
|
+
if (!sodiumIsReady) {
|
|
295
|
+
yield import_libsodium_wrappers_sumo3.default.ready;
|
|
296
|
+
sodiumIsReady = true;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
var UppyEncryptPlugin = class extends import_core.BasePlugin {
|
|
300
|
+
constructor(uppy, opts) {
|
|
301
|
+
var _a;
|
|
302
|
+
super(uppy, opts);
|
|
303
|
+
this.id = (_a = opts == null ? void 0 : opts.id) != null ? _a : "UppyEncryptPlugin";
|
|
304
|
+
this.type = "modifier";
|
|
305
|
+
const defaultOptions = {
|
|
306
|
+
password: null
|
|
307
|
+
};
|
|
308
|
+
this.opts = __spreadValues(__spreadValues({}, defaultOptions), opts);
|
|
309
|
+
this.encryptFiles = this.encryptFiles.bind(this);
|
|
310
|
+
}
|
|
311
|
+
encryptFiles(fileIds) {
|
|
312
|
+
return __async(this, null, function* () {
|
|
313
|
+
this.opts.password = this.opts.password || UppyEncrypt.generatePassword();
|
|
314
|
+
this.uppy.setMeta({ password: this.opts.password });
|
|
315
|
+
for (const fileId of fileIds) {
|
|
316
|
+
const file = this.uppy.getFile(fileId);
|
|
317
|
+
const enc = new UppyEncrypt(this.uppy, file, this.opts.password);
|
|
318
|
+
if (yield enc.encryptFile()) {
|
|
319
|
+
this.uppy.emit("preprocess-complete", file);
|
|
320
|
+
let blob = yield enc.getEncryptedFile();
|
|
321
|
+
this.uppy.setFileState(fileId, {
|
|
322
|
+
type: "application/octet-stream",
|
|
323
|
+
data: blob,
|
|
324
|
+
size: blob.size
|
|
325
|
+
});
|
|
326
|
+
this.uppy.setFileMeta(fileId, {
|
|
327
|
+
name: `${file.name}.enc`,
|
|
328
|
+
type: "application/octet-stream",
|
|
329
|
+
encryption: {
|
|
330
|
+
salt: enc.getSalt(),
|
|
331
|
+
header: enc.getHeader(),
|
|
332
|
+
hash: enc.getPasswordHash(),
|
|
333
|
+
meta: enc.getEncryptMetaData()
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
install() {
|
|
341
|
+
this.uppy.addPreProcessor(this.encryptFiles);
|
|
342
|
+
}
|
|
343
|
+
uninstall() {
|
|
344
|
+
this.uppy.removePreProcessor(this.encryptFiles);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
348
|
+
0 && (module.exports = {
|
|
349
|
+
UppyDecrypt,
|
|
350
|
+
UppyEncrypt,
|
|
351
|
+
UppyEncryptPlugin,
|
|
352
|
+
uppyEncryptReady
|
|
353
|
+
});
|
|
354
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/UppyEncrypt.ts","../src/constants.ts","../src/UppyDecrypt.ts"],"sourcesContent":["import UppyEncrypt from './UppyEncrypt';\nimport UppyDecrypt, { type DecryptedMetaData } from './UppyDecrypt';\nimport _sodium from 'libsodium-wrappers-sumo';\nimport { BasePlugin, type DefaultPluginOptions, Uppy } from '@uppy/core';\n\ninterface UppyEncryptPluginOptions extends DefaultPluginOptions {\n password: string | null;\n}\n\n// Sodium is initialized automatically within UppyEncrypt / UppyDecrypt\n// Optionally call this to ensure initialization\nlet sodiumIsReady = false;\nexport const uppyEncryptReady = async () => {\n if (!sodiumIsReady) {\n await _sodium.ready;\n sodiumIsReady = true;\n }\n};\n\nexport class UppyEncryptPlugin extends BasePlugin {\n opts: UppyEncryptPluginOptions;\n\n constructor(uppy: Uppy, opts?: UppyEncryptPluginOptions | undefined) {\n super(uppy, opts);\n this.id = opts?.id ?? 'UppyEncryptPlugin';\n this.type = 'modifier';\n\n const defaultOptions = {\n password: null,\n };\n this.opts = { ...defaultOptions, ...opts };\n\n this.encryptFiles = this.encryptFiles.bind(this);\n }\n\n async encryptFiles(fileIds: string[]) {\n // Generate a password here if none is already set\n this.opts.password = this.opts.password || UppyEncrypt.generatePassword();\n\n // Add password to meta data so it can be referenced externally\n this.uppy.setMeta({ password: this.opts.password });\n\n for (const fileId of fileIds) {\n const file = this.uppy.getFile(fileId);\n const enc = new UppyEncrypt(this.uppy, file, this.opts.password);\n if (await enc.encryptFile()) {\n this.uppy.emit('preprocess-complete', file);\n let blob = await enc.getEncryptedFile();\n this.uppy.setFileState(fileId, {\n type: 'application/octet-stream',\n data: blob,\n size: blob.size,\n });\n\n this.uppy.setFileMeta(fileId, {\n name: `${file.name}.enc`,\n type: 'application/octet-stream',\n encryption: {\n salt: enc.getSalt(),\n header: enc.getHeader(),\n hash: enc.getPasswordHash(),\n meta: enc.getEncryptMetaData(),\n },\n });\n }\n }\n }\n\n install() {\n this.uppy.addPreProcessor(this.encryptFiles);\n }\n\n uninstall() {\n this.uppy.removePreProcessor(this.encryptFiles);\n }\n}\n\nexport { UppyEncrypt, UppyDecrypt, DecryptedMetaData };\n","import type { Uppy, UppyFile } from '@uppy/core';\nimport _sodium from 'libsodium-wrappers-sumo';\nimport { CHUNK_SIZE, SIGNATURE } from './constants';\n\n// Init Sodium\nlet sodium: typeof _sodium;\n(async () => {\n await _sodium.ready;\n sodium = _sodium;\n})();\n\nexport default class UppyEncrypt {\n private uppy: Uppy;\n private password: string;\n private salt: Uint8Array;\n private key: Uint8Array;\n private state: _sodium.StateAddress;\n private header: Uint8Array;\n private file: UppyFile<Record<string, unknown>, Record<string, unknown>>;\n private stream: ReadableStream;\n private streamController: ReadableStreamDefaultController | undefined;\n private streamCanceled = false;\n\n private index = 0;\n\n constructor(uppy: Uppy, file: UppyFile<Record<string, unknown>, Record<string, unknown>>, password: string) {\n this.uppy = uppy;\n this.file = file;\n this.password = password;\n\n // Set Uppy event handlers that effect the encryption process\n uppy.on('cancel-all', () => {\n this.streamCanceled = true;\n });\n\n this.streamController;\n this.stream = new ReadableStream({\n start: (controller) => {\n this.streamController = controller;\n },\n });\n\n this.salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);\n this.key = sodium.crypto_pwhash(\n sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES,\n password,\n this.salt,\n sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,\n sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,\n sodium.crypto_pwhash_ALG_ARGON2ID13\n );\n\n const res = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);\n this.state = res.state;\n this.header = res.header;\n }\n\n /**\n * Helper function that generate a random password\n */\n static generatePassword() {\n return sodium.to_base64(sodium.randombytes_buf(16), sodium.base64_variants.URLSAFE_NO_PADDING);\n }\n\n /**\n * Encrypts the file\n */\n async encryptFile() {\n if (!this.streamController) {\n throw new Error('Encryption stream does not exist');\n }\n\n while (this.index < this.file.size) {\n if (this.streamCanceled) {\n await this.stream.cancel();\n return false;\n }\n\n // If first chunk\n if (this.index === 0) {\n this.streamController.enqueue(new Uint8Array(new TextEncoder().encode(SIGNATURE)));\n this.streamController.enqueue(this.salt);\n this.streamController.enqueue(this.header);\n }\n\n const tag =\n this.index + CHUNK_SIZE < this.file.size\n ? sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE\n : sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL;\n\n const chunk = await this.file.data.slice(this.index, this.index + CHUNK_SIZE).arrayBuffer();\n const encryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_push(this.state, new Uint8Array(chunk), null, tag);\n\n this.streamController.enqueue(new Uint8Array(encryptedChunk));\n\n this.uppy.emit('preprocess-progress', this.file, {\n mode: 'determinate',\n message: `Encrypting ${this.file.name}...`,\n value: this.index / this.file.size,\n });\n\n this.index += CHUNK_SIZE;\n }\n\n this.uppy.emit('preprocess-progress', this.file, {\n mode: 'determinate',\n message: `Encrypting ${this.file.name}...`,\n value: 1,\n });\n\n this.streamController.close();\n\n return true;\n }\n\n /**\n * Creates and returns a Blob of the encrypted file\n */\n async getEncryptedFile() {\n const response = new Response(this.stream);\n return response.blob();\n }\n\n /**\n * Returns an encrypted representation of the file's metadata (name, content-type)\n * header: base64-encoded header data\n * meta: Encrypted JSON string of the file's metadata, base64-encoded\n */\n getEncryptMetaData() {\n // Init fresh state\n const res = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);\n\n const metaJson = JSON.stringify({ name: this.file.meta.name, type: this.file.meta.type || null });\n const encryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_push(\n res.state,\n new Uint8Array(new TextEncoder().encode(metaJson)),\n null,\n sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL\n );\n\n return {\n header: sodium.to_base64(res.header, sodium.base64_variants.URLSAFE_NO_PADDING),\n data: sodium.to_base64(encryptedChunk, sodium.base64_variants.URLSAFE_NO_PADDING),\n };\n }\n\n /**\n * Returns a hash of the password base64-encoded\n * This data is safe to store in a database, etc\n */\n getPasswordHash() {\n return sodium.crypto_pwhash_str(this.password, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);\n }\n\n /**\n * Returns the header base64-encoded\n * This data is safe to store in a database, etc\n */\n getHeader() {\n return sodium.to_base64(this.header, sodium.base64_variants.URLSAFE_NO_PADDING);\n }\n\n /**\n * Returns the salt base64-encoded\n * This data is safe to store in a database, etc\n */\n getSalt() {\n return sodium.to_base64(this.salt, sodium.base64_variants.URLSAFE_NO_PADDING);\n }\n}\n","export const CHUNK_SIZE = 64 * 1024 * 1024;\nexport const SIGNATURE = 'uppyencrypt';\n","import _sodium from 'libsodium-wrappers-sumo';\nimport { CHUNK_SIZE, SIGNATURE } from './constants';\n\nexport interface DecryptedMetaData {\n name: string;\n type?: string;\n}\n\n// Init Sodium\nlet sodium: typeof _sodium;\n(async () => {\n await _sodium.ready;\n sodium = _sodium;\n})();\n\nexport default class UppyDecrypt {\n private key: Uint8Array;\n private state: _sodium.StateAddress;\n private stream: ReadableStream;\n private streamController: ReadableStreamDefaultController | undefined;\n private contentType: string;\n\n private index = 0;\n\n constructor(password: string, salt: string, header: string) {\n const saltUint = sodium.from_base64(salt, sodium.base64_variants.URLSAFE_NO_PADDING);\n const headerUint = sodium.from_base64(header, sodium.base64_variants.URLSAFE_NO_PADDING);\n\n this.streamController;\n this.stream = new ReadableStream({\n start: (controller) => {\n this.streamController = controller;\n },\n });\n this.contentType = ''; // Defined if/when meta-data is decrypted\n\n this.key = sodium.crypto_pwhash(\n sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES,\n password,\n saltUint,\n sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,\n sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,\n sodium.crypto_pwhash_ALG_ARGON2ID13\n );\n\n this.state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(headerUint, this.key);\n\n this.index = SIGNATURE.length + saltUint.length + headerUint.length;\n }\n\n /**\n * Validates that the provided password is correct\n * @param hash The hash value of the password created during UppyEncrypt\n * @param password The user-provided password\n * @returns {bool} true if correct password\n */\n static verifyPassword(hash: string, password: string) {\n return sodium.crypto_pwhash_str_verify(hash, password);\n }\n\n /**\n * Decrypts the provided file\n * @param file Blob of encryptyed file\n * @returns Decrypted file as a blob\n */\n async decryptFile(file: Blob) {\n if (!this.streamController) {\n throw new Error('Encryption stream does not exist');\n }\n\n while (this.index < file.size) {\n const chunk = await file.slice(this.index, this.index + CHUNK_SIZE + sodium.crypto_secretstream_xchacha20poly1305_ABYTES).arrayBuffer();\n const decryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_pull(this.state, new Uint8Array(chunk));\n\n this.streamController.enqueue(decryptedChunk.message);\n\n this.index += CHUNK_SIZE + sodium.crypto_secretstream_xchacha20poly1305_ABYTES;\n }\n\n this.streamController.close();\n\n const response = new Response(this.stream, { headers: { 'Content-Type': this.contentType } });\n return response.blob();\n }\n\n /**\n *\n * @param header Header created during encryption of the meta data\n * @param meta Encrypted meta data string\n * @returns object of the decrypted meta data\n */\n getDecryptedMetaData(header: string, meta: string) {\n // Init fresh state\n const state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(sodium.from_base64(header, sodium.base64_variants.URLSAFE_NO_PADDING), this.key);\n const decryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_pull(state, sodium.from_base64(meta, sodium.base64_variants.URLSAFE_NO_PADDING));\n\n if (!decryptedChunk) throw new Error('Unable to decrypt meta data');\n const decryptedMeta = JSON.parse(new TextDecoder().decode(decryptedChunk.message)) as DecryptedMetaData;\n if (decryptedMeta.type) this.contentType = decryptedMeta.type;\n return decryptedMeta;\n }\n}\n"],"mappings":"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","names":["_sodium","import_libsodium_wrappers_sumo","sodium","_sodium","import_libsodium_wrappers_sumo","_sodium"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
17
|
+
var __async = (__this, __arguments, generator) => {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
var fulfilled = (value) => {
|
|
20
|
+
try {
|
|
21
|
+
step(generator.next(value));
|
|
22
|
+
} catch (e) {
|
|
23
|
+
reject(e);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var rejected = (value) => {
|
|
27
|
+
try {
|
|
28
|
+
step(generator.throw(value));
|
|
29
|
+
} catch (e) {
|
|
30
|
+
reject(e);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
34
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/UppyEncrypt.ts
|
|
39
|
+
import _sodium from "libsodium-wrappers-sumo";
|
|
40
|
+
|
|
41
|
+
// src/constants.ts
|
|
42
|
+
var CHUNK_SIZE = 64 * 1024 * 1024;
|
|
43
|
+
var SIGNATURE = "uppyencrypt";
|
|
44
|
+
|
|
45
|
+
// src/UppyEncrypt.ts
|
|
46
|
+
var sodium;
|
|
47
|
+
(() => __async(void 0, null, function* () {
|
|
48
|
+
yield _sodium.ready;
|
|
49
|
+
sodium = _sodium;
|
|
50
|
+
}))();
|
|
51
|
+
var UppyEncrypt = class {
|
|
52
|
+
constructor(uppy, file, password) {
|
|
53
|
+
this.streamCanceled = false;
|
|
54
|
+
this.index = 0;
|
|
55
|
+
this.uppy = uppy;
|
|
56
|
+
this.file = file;
|
|
57
|
+
this.password = password;
|
|
58
|
+
uppy.on("cancel-all", () => {
|
|
59
|
+
this.streamCanceled = true;
|
|
60
|
+
});
|
|
61
|
+
this.streamController;
|
|
62
|
+
this.stream = new ReadableStream({
|
|
63
|
+
start: (controller) => {
|
|
64
|
+
this.streamController = controller;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
this.salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);
|
|
68
|
+
this.key = sodium.crypto_pwhash(
|
|
69
|
+
sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES,
|
|
70
|
+
password,
|
|
71
|
+
this.salt,
|
|
72
|
+
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
73
|
+
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
74
|
+
sodium.crypto_pwhash_ALG_ARGON2ID13
|
|
75
|
+
);
|
|
76
|
+
const res = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);
|
|
77
|
+
this.state = res.state;
|
|
78
|
+
this.header = res.header;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Helper function that generate a random password
|
|
82
|
+
*/
|
|
83
|
+
static generatePassword() {
|
|
84
|
+
return sodium.to_base64(sodium.randombytes_buf(16), sodium.base64_variants.URLSAFE_NO_PADDING);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Encrypts the file
|
|
88
|
+
*/
|
|
89
|
+
encryptFile() {
|
|
90
|
+
return __async(this, null, function* () {
|
|
91
|
+
if (!this.streamController) {
|
|
92
|
+
throw new Error("Encryption stream does not exist");
|
|
93
|
+
}
|
|
94
|
+
while (this.index < this.file.size) {
|
|
95
|
+
if (this.streamCanceled) {
|
|
96
|
+
yield this.stream.cancel();
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (this.index === 0) {
|
|
100
|
+
this.streamController.enqueue(new Uint8Array(new TextEncoder().encode(SIGNATURE)));
|
|
101
|
+
this.streamController.enqueue(this.salt);
|
|
102
|
+
this.streamController.enqueue(this.header);
|
|
103
|
+
}
|
|
104
|
+
const tag = this.index + CHUNK_SIZE < this.file.size ? sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE : sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL;
|
|
105
|
+
const chunk = yield this.file.data.slice(this.index, this.index + CHUNK_SIZE).arrayBuffer();
|
|
106
|
+
const encryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_push(this.state, new Uint8Array(chunk), null, tag);
|
|
107
|
+
this.streamController.enqueue(new Uint8Array(encryptedChunk));
|
|
108
|
+
this.uppy.emit("preprocess-progress", this.file, {
|
|
109
|
+
mode: "determinate",
|
|
110
|
+
message: `Encrypting ${this.file.name}...`,
|
|
111
|
+
value: this.index / this.file.size
|
|
112
|
+
});
|
|
113
|
+
this.index += CHUNK_SIZE;
|
|
114
|
+
}
|
|
115
|
+
this.uppy.emit("preprocess-progress", this.file, {
|
|
116
|
+
mode: "determinate",
|
|
117
|
+
message: `Encrypting ${this.file.name}...`,
|
|
118
|
+
value: 1
|
|
119
|
+
});
|
|
120
|
+
this.streamController.close();
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Creates and returns a Blob of the encrypted file
|
|
126
|
+
*/
|
|
127
|
+
getEncryptedFile() {
|
|
128
|
+
return __async(this, null, function* () {
|
|
129
|
+
const response = new Response(this.stream);
|
|
130
|
+
return response.blob();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Returns an encrypted representation of the file's metadata (name, content-type)
|
|
135
|
+
* header: base64-encoded header data
|
|
136
|
+
* meta: Encrypted JSON string of the file's metadata, base64-encoded
|
|
137
|
+
*/
|
|
138
|
+
getEncryptMetaData() {
|
|
139
|
+
const res = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);
|
|
140
|
+
const metaJson = JSON.stringify({ name: this.file.meta.name, type: this.file.meta.type || null });
|
|
141
|
+
const encryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_push(
|
|
142
|
+
res.state,
|
|
143
|
+
new Uint8Array(new TextEncoder().encode(metaJson)),
|
|
144
|
+
null,
|
|
145
|
+
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
|
|
146
|
+
);
|
|
147
|
+
return {
|
|
148
|
+
header: sodium.to_base64(res.header, sodium.base64_variants.URLSAFE_NO_PADDING),
|
|
149
|
+
data: sodium.to_base64(encryptedChunk, sodium.base64_variants.URLSAFE_NO_PADDING)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Returns a hash of the password base64-encoded
|
|
154
|
+
* This data is safe to store in a database, etc
|
|
155
|
+
*/
|
|
156
|
+
getPasswordHash() {
|
|
157
|
+
return sodium.crypto_pwhash_str(this.password, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Returns the header base64-encoded
|
|
161
|
+
* This data is safe to store in a database, etc
|
|
162
|
+
*/
|
|
163
|
+
getHeader() {
|
|
164
|
+
return sodium.to_base64(this.header, sodium.base64_variants.URLSAFE_NO_PADDING);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Returns the salt base64-encoded
|
|
168
|
+
* This data is safe to store in a database, etc
|
|
169
|
+
*/
|
|
170
|
+
getSalt() {
|
|
171
|
+
return sodium.to_base64(this.salt, sodium.base64_variants.URLSAFE_NO_PADDING);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// src/UppyDecrypt.ts
|
|
176
|
+
import _sodium2 from "libsodium-wrappers-sumo";
|
|
177
|
+
var sodium2;
|
|
178
|
+
(() => __async(void 0, null, function* () {
|
|
179
|
+
yield _sodium2.ready;
|
|
180
|
+
sodium2 = _sodium2;
|
|
181
|
+
}))();
|
|
182
|
+
var UppyDecrypt = class {
|
|
183
|
+
constructor(password, salt, header) {
|
|
184
|
+
this.index = 0;
|
|
185
|
+
const saltUint = sodium2.from_base64(salt, sodium2.base64_variants.URLSAFE_NO_PADDING);
|
|
186
|
+
const headerUint = sodium2.from_base64(header, sodium2.base64_variants.URLSAFE_NO_PADDING);
|
|
187
|
+
this.streamController;
|
|
188
|
+
this.stream = new ReadableStream({
|
|
189
|
+
start: (controller) => {
|
|
190
|
+
this.streamController = controller;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
this.contentType = "";
|
|
194
|
+
this.key = sodium2.crypto_pwhash(
|
|
195
|
+
sodium2.crypto_secretstream_xchacha20poly1305_KEYBYTES,
|
|
196
|
+
password,
|
|
197
|
+
saltUint,
|
|
198
|
+
sodium2.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
199
|
+
sodium2.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
200
|
+
sodium2.crypto_pwhash_ALG_ARGON2ID13
|
|
201
|
+
);
|
|
202
|
+
this.state = sodium2.crypto_secretstream_xchacha20poly1305_init_pull(headerUint, this.key);
|
|
203
|
+
this.index = SIGNATURE.length + saltUint.length + headerUint.length;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Validates that the provided password is correct
|
|
207
|
+
* @param hash The hash value of the password created during UppyEncrypt
|
|
208
|
+
* @param password The user-provided password
|
|
209
|
+
* @returns {bool} true if correct password
|
|
210
|
+
*/
|
|
211
|
+
static verifyPassword(hash, password) {
|
|
212
|
+
return sodium2.crypto_pwhash_str_verify(hash, password);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Decrypts the provided file
|
|
216
|
+
* @param file Blob of encryptyed file
|
|
217
|
+
* @returns Decrypted file as a blob
|
|
218
|
+
*/
|
|
219
|
+
decryptFile(file) {
|
|
220
|
+
return __async(this, null, function* () {
|
|
221
|
+
if (!this.streamController) {
|
|
222
|
+
throw new Error("Encryption stream does not exist");
|
|
223
|
+
}
|
|
224
|
+
while (this.index < file.size) {
|
|
225
|
+
const chunk = yield file.slice(this.index, this.index + CHUNK_SIZE + sodium2.crypto_secretstream_xchacha20poly1305_ABYTES).arrayBuffer();
|
|
226
|
+
const decryptedChunk = sodium2.crypto_secretstream_xchacha20poly1305_pull(this.state, new Uint8Array(chunk));
|
|
227
|
+
this.streamController.enqueue(decryptedChunk.message);
|
|
228
|
+
this.index += CHUNK_SIZE + sodium2.crypto_secretstream_xchacha20poly1305_ABYTES;
|
|
229
|
+
}
|
|
230
|
+
this.streamController.close();
|
|
231
|
+
const response = new Response(this.stream, { headers: { "Content-Type": this.contentType } });
|
|
232
|
+
return response.blob();
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
*
|
|
237
|
+
* @param header Header created during encryption of the meta data
|
|
238
|
+
* @param meta Encrypted meta data string
|
|
239
|
+
* @returns object of the decrypted meta data
|
|
240
|
+
*/
|
|
241
|
+
getDecryptedMetaData(header, meta) {
|
|
242
|
+
const state = sodium2.crypto_secretstream_xchacha20poly1305_init_pull(sodium2.from_base64(header, sodium2.base64_variants.URLSAFE_NO_PADDING), this.key);
|
|
243
|
+
const decryptedChunk = sodium2.crypto_secretstream_xchacha20poly1305_pull(state, sodium2.from_base64(meta, sodium2.base64_variants.URLSAFE_NO_PADDING));
|
|
244
|
+
if (!decryptedChunk)
|
|
245
|
+
throw new Error("Unable to decrypt meta data");
|
|
246
|
+
const decryptedMeta = JSON.parse(new TextDecoder().decode(decryptedChunk.message));
|
|
247
|
+
if (decryptedMeta.type)
|
|
248
|
+
this.contentType = decryptedMeta.type;
|
|
249
|
+
return decryptedMeta;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// src/index.ts
|
|
254
|
+
import _sodium3 from "libsodium-wrappers-sumo";
|
|
255
|
+
import { BasePlugin } from "@uppy/core";
|
|
256
|
+
var sodiumIsReady = false;
|
|
257
|
+
var uppyEncryptReady = () => __async(void 0, null, function* () {
|
|
258
|
+
if (!sodiumIsReady) {
|
|
259
|
+
yield _sodium3.ready;
|
|
260
|
+
sodiumIsReady = true;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
var UppyEncryptPlugin = class extends BasePlugin {
|
|
264
|
+
constructor(uppy, opts) {
|
|
265
|
+
var _a;
|
|
266
|
+
super(uppy, opts);
|
|
267
|
+
this.id = (_a = opts == null ? void 0 : opts.id) != null ? _a : "UppyEncryptPlugin";
|
|
268
|
+
this.type = "modifier";
|
|
269
|
+
const defaultOptions = {
|
|
270
|
+
password: null
|
|
271
|
+
};
|
|
272
|
+
this.opts = __spreadValues(__spreadValues({}, defaultOptions), opts);
|
|
273
|
+
this.encryptFiles = this.encryptFiles.bind(this);
|
|
274
|
+
}
|
|
275
|
+
encryptFiles(fileIds) {
|
|
276
|
+
return __async(this, null, function* () {
|
|
277
|
+
this.opts.password = this.opts.password || UppyEncrypt.generatePassword();
|
|
278
|
+
this.uppy.setMeta({ password: this.opts.password });
|
|
279
|
+
for (const fileId of fileIds) {
|
|
280
|
+
const file = this.uppy.getFile(fileId);
|
|
281
|
+
const enc = new UppyEncrypt(this.uppy, file, this.opts.password);
|
|
282
|
+
if (yield enc.encryptFile()) {
|
|
283
|
+
this.uppy.emit("preprocess-complete", file);
|
|
284
|
+
let blob = yield enc.getEncryptedFile();
|
|
285
|
+
this.uppy.setFileState(fileId, {
|
|
286
|
+
type: "application/octet-stream",
|
|
287
|
+
data: blob,
|
|
288
|
+
size: blob.size
|
|
289
|
+
});
|
|
290
|
+
this.uppy.setFileMeta(fileId, {
|
|
291
|
+
name: `${file.name}.enc`,
|
|
292
|
+
type: "application/octet-stream",
|
|
293
|
+
encryption: {
|
|
294
|
+
salt: enc.getSalt(),
|
|
295
|
+
header: enc.getHeader(),
|
|
296
|
+
hash: enc.getPasswordHash(),
|
|
297
|
+
meta: enc.getEncryptMetaData()
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
install() {
|
|
305
|
+
this.uppy.addPreProcessor(this.encryptFiles);
|
|
306
|
+
}
|
|
307
|
+
uninstall() {
|
|
308
|
+
this.uppy.removePreProcessor(this.encryptFiles);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
export {
|
|
312
|
+
UppyDecrypt,
|
|
313
|
+
UppyEncrypt,
|
|
314
|
+
UppyEncryptPlugin,
|
|
315
|
+
uppyEncryptReady
|
|
316
|
+
};
|
|
317
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/UppyEncrypt.ts","../src/constants.ts","../src/UppyDecrypt.ts","../src/index.ts"],"sourcesContent":["import type { Uppy, UppyFile } from '@uppy/core';\nimport _sodium from 'libsodium-wrappers-sumo';\nimport { CHUNK_SIZE, SIGNATURE } from './constants';\n\n// Init Sodium\nlet sodium: typeof _sodium;\n(async () => {\n await _sodium.ready;\n sodium = _sodium;\n})();\n\nexport default class UppyEncrypt {\n private uppy: Uppy;\n private password: string;\n private salt: Uint8Array;\n private key: Uint8Array;\n private state: _sodium.StateAddress;\n private header: Uint8Array;\n private file: UppyFile<Record<string, unknown>, Record<string, unknown>>;\n private stream: ReadableStream;\n private streamController: ReadableStreamDefaultController | undefined;\n private streamCanceled = false;\n\n private index = 0;\n\n constructor(uppy: Uppy, file: UppyFile<Record<string, unknown>, Record<string, unknown>>, password: string) {\n this.uppy = uppy;\n this.file = file;\n this.password = password;\n\n // Set Uppy event handlers that effect the encryption process\n uppy.on('cancel-all', () => {\n this.streamCanceled = true;\n });\n\n this.streamController;\n this.stream = new ReadableStream({\n start: (controller) => {\n this.streamController = controller;\n },\n });\n\n this.salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);\n this.key = sodium.crypto_pwhash(\n sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES,\n password,\n this.salt,\n sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,\n sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,\n sodium.crypto_pwhash_ALG_ARGON2ID13\n );\n\n const res = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);\n this.state = res.state;\n this.header = res.header;\n }\n\n /**\n * Helper function that generate a random password\n */\n static generatePassword() {\n return sodium.to_base64(sodium.randombytes_buf(16), sodium.base64_variants.URLSAFE_NO_PADDING);\n }\n\n /**\n * Encrypts the file\n */\n async encryptFile() {\n if (!this.streamController) {\n throw new Error('Encryption stream does not exist');\n }\n\n while (this.index < this.file.size) {\n if (this.streamCanceled) {\n await this.stream.cancel();\n return false;\n }\n\n // If first chunk\n if (this.index === 0) {\n this.streamController.enqueue(new Uint8Array(new TextEncoder().encode(SIGNATURE)));\n this.streamController.enqueue(this.salt);\n this.streamController.enqueue(this.header);\n }\n\n const tag =\n this.index + CHUNK_SIZE < this.file.size\n ? sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE\n : sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL;\n\n const chunk = await this.file.data.slice(this.index, this.index + CHUNK_SIZE).arrayBuffer();\n const encryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_push(this.state, new Uint8Array(chunk), null, tag);\n\n this.streamController.enqueue(new Uint8Array(encryptedChunk));\n\n this.uppy.emit('preprocess-progress', this.file, {\n mode: 'determinate',\n message: `Encrypting ${this.file.name}...`,\n value: this.index / this.file.size,\n });\n\n this.index += CHUNK_SIZE;\n }\n\n this.uppy.emit('preprocess-progress', this.file, {\n mode: 'determinate',\n message: `Encrypting ${this.file.name}...`,\n value: 1,\n });\n\n this.streamController.close();\n\n return true;\n }\n\n /**\n * Creates and returns a Blob of the encrypted file\n */\n async getEncryptedFile() {\n const response = new Response(this.stream);\n return response.blob();\n }\n\n /**\n * Returns an encrypted representation of the file's metadata (name, content-type)\n * header: base64-encoded header data\n * meta: Encrypted JSON string of the file's metadata, base64-encoded\n */\n getEncryptMetaData() {\n // Init fresh state\n const res = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);\n\n const metaJson = JSON.stringify({ name: this.file.meta.name, type: this.file.meta.type || null });\n const encryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_push(\n res.state,\n new Uint8Array(new TextEncoder().encode(metaJson)),\n null,\n sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL\n );\n\n return {\n header: sodium.to_base64(res.header, sodium.base64_variants.URLSAFE_NO_PADDING),\n data: sodium.to_base64(encryptedChunk, sodium.base64_variants.URLSAFE_NO_PADDING),\n };\n }\n\n /**\n * Returns a hash of the password base64-encoded\n * This data is safe to store in a database, etc\n */\n getPasswordHash() {\n return sodium.crypto_pwhash_str(this.password, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);\n }\n\n /**\n * Returns the header base64-encoded\n * This data is safe to store in a database, etc\n */\n getHeader() {\n return sodium.to_base64(this.header, sodium.base64_variants.URLSAFE_NO_PADDING);\n }\n\n /**\n * Returns the salt base64-encoded\n * This data is safe to store in a database, etc\n */\n getSalt() {\n return sodium.to_base64(this.salt, sodium.base64_variants.URLSAFE_NO_PADDING);\n }\n}\n","export const CHUNK_SIZE = 64 * 1024 * 1024;\nexport const SIGNATURE = 'uppyencrypt';\n","import _sodium from 'libsodium-wrappers-sumo';\nimport { CHUNK_SIZE, SIGNATURE } from './constants';\n\nexport interface DecryptedMetaData {\n name: string;\n type?: string;\n}\n\n// Init Sodium\nlet sodium: typeof _sodium;\n(async () => {\n await _sodium.ready;\n sodium = _sodium;\n})();\n\nexport default class UppyDecrypt {\n private key: Uint8Array;\n private state: _sodium.StateAddress;\n private stream: ReadableStream;\n private streamController: ReadableStreamDefaultController | undefined;\n private contentType: string;\n\n private index = 0;\n\n constructor(password: string, salt: string, header: string) {\n const saltUint = sodium.from_base64(salt, sodium.base64_variants.URLSAFE_NO_PADDING);\n const headerUint = sodium.from_base64(header, sodium.base64_variants.URLSAFE_NO_PADDING);\n\n this.streamController;\n this.stream = new ReadableStream({\n start: (controller) => {\n this.streamController = controller;\n },\n });\n this.contentType = ''; // Defined if/when meta-data is decrypted\n\n this.key = sodium.crypto_pwhash(\n sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES,\n password,\n saltUint,\n sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,\n sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,\n sodium.crypto_pwhash_ALG_ARGON2ID13\n );\n\n this.state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(headerUint, this.key);\n\n this.index = SIGNATURE.length + saltUint.length + headerUint.length;\n }\n\n /**\n * Validates that the provided password is correct\n * @param hash The hash value of the password created during UppyEncrypt\n * @param password The user-provided password\n * @returns {bool} true if correct password\n */\n static verifyPassword(hash: string, password: string) {\n return sodium.crypto_pwhash_str_verify(hash, password);\n }\n\n /**\n * Decrypts the provided file\n * @param file Blob of encryptyed file\n * @returns Decrypted file as a blob\n */\n async decryptFile(file: Blob) {\n if (!this.streamController) {\n throw new Error('Encryption stream does not exist');\n }\n\n while (this.index < file.size) {\n const chunk = await file.slice(this.index, this.index + CHUNK_SIZE + sodium.crypto_secretstream_xchacha20poly1305_ABYTES).arrayBuffer();\n const decryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_pull(this.state, new Uint8Array(chunk));\n\n this.streamController.enqueue(decryptedChunk.message);\n\n this.index += CHUNK_SIZE + sodium.crypto_secretstream_xchacha20poly1305_ABYTES;\n }\n\n this.streamController.close();\n\n const response = new Response(this.stream, { headers: { 'Content-Type': this.contentType } });\n return response.blob();\n }\n\n /**\n *\n * @param header Header created during encryption of the meta data\n * @param meta Encrypted meta data string\n * @returns object of the decrypted meta data\n */\n getDecryptedMetaData(header: string, meta: string) {\n // Init fresh state\n const state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(sodium.from_base64(header, sodium.base64_variants.URLSAFE_NO_PADDING), this.key);\n const decryptedChunk = sodium.crypto_secretstream_xchacha20poly1305_pull(state, sodium.from_base64(meta, sodium.base64_variants.URLSAFE_NO_PADDING));\n\n if (!decryptedChunk) throw new Error('Unable to decrypt meta data');\n const decryptedMeta = JSON.parse(new TextDecoder().decode(decryptedChunk.message)) as DecryptedMetaData;\n if (decryptedMeta.type) this.contentType = decryptedMeta.type;\n return decryptedMeta;\n }\n}\n","import UppyEncrypt from './UppyEncrypt';\nimport UppyDecrypt, { type DecryptedMetaData } from './UppyDecrypt';\nimport _sodium from 'libsodium-wrappers-sumo';\nimport { BasePlugin, type DefaultPluginOptions, Uppy } from '@uppy/core';\n\ninterface UppyEncryptPluginOptions extends DefaultPluginOptions {\n password: string | null;\n}\n\n// Sodium is initialized automatically within UppyEncrypt / UppyDecrypt\n// Optionally call this to ensure initialization\nlet sodiumIsReady = false;\nexport const uppyEncryptReady = async () => {\n if (!sodiumIsReady) {\n await _sodium.ready;\n sodiumIsReady = true;\n }\n};\n\nexport class UppyEncryptPlugin extends BasePlugin {\n opts: UppyEncryptPluginOptions;\n\n constructor(uppy: Uppy, opts?: UppyEncryptPluginOptions | undefined) {\n super(uppy, opts);\n this.id = opts?.id ?? 'UppyEncryptPlugin';\n this.type = 'modifier';\n\n const defaultOptions = {\n password: null,\n };\n this.opts = { ...defaultOptions, ...opts };\n\n this.encryptFiles = this.encryptFiles.bind(this);\n }\n\n async encryptFiles(fileIds: string[]) {\n // Generate a password here if none is already set\n this.opts.password = this.opts.password || UppyEncrypt.generatePassword();\n\n // Add password to meta data so it can be referenced externally\n this.uppy.setMeta({ password: this.opts.password });\n\n for (const fileId of fileIds) {\n const file = this.uppy.getFile(fileId);\n const enc = new UppyEncrypt(this.uppy, file, this.opts.password);\n if (await enc.encryptFile()) {\n this.uppy.emit('preprocess-complete', file);\n let blob = await enc.getEncryptedFile();\n this.uppy.setFileState(fileId, {\n type: 'application/octet-stream',\n data: blob,\n size: blob.size,\n });\n\n this.uppy.setFileMeta(fileId, {\n name: `${file.name}.enc`,\n type: 'application/octet-stream',\n encryption: {\n salt: enc.getSalt(),\n header: enc.getHeader(),\n hash: enc.getPasswordHash(),\n meta: enc.getEncryptMetaData(),\n },\n });\n }\n }\n }\n\n install() {\n this.uppy.addPreProcessor(this.encryptFiles);\n }\n\n uninstall() {\n this.uppy.removePreProcessor(this.encryptFiles);\n }\n}\n\nexport { UppyEncrypt, UppyDecrypt, DecryptedMetaData };\n"],"mappings":"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","names":["_sodium","sodium","_sodium","_sodium","_sodium"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "uppy-encrypt",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Uppy plugin to encrypt and decrypt files in the browser before upload using libsodium-wrappers",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/0sumcode/uppy-encrypt.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"uppy",
|
|
21
|
+
"encrypt",
|
|
22
|
+
"decrypt",
|
|
23
|
+
"libsodium"
|
|
24
|
+
],
|
|
25
|
+
"author": "Dan Stevens",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/0sumcode/uppy-encrypt/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/0sumcode/uppy-encrypt#readme",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"ts-node": "^10.9.1",
|
|
33
|
+
"tsup": "^8.0.1",
|
|
34
|
+
"typescript": "^5.3.2"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@types/libsodium-wrappers-sumo": "^0.7.8",
|
|
38
|
+
"@uppy/core": "^3.7.1",
|
|
39
|
+
"libsodium-wrappers-sumo": "^0.7.13"
|
|
40
|
+
}
|
|
41
|
+
}
|