@secrecy/lib 1.10.1 → 1.11.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/dist/lib/base-client.js +1 -1
- package/dist/lib/cache.js +9 -0
- package/dist/lib/client/SecrecyCloudClient.js +19 -14
- package/dist/lib/client/SecrecyMailClient.js +3 -4
- package/dist/lib/client/convert/node.js +3 -3
- package/dist/lib/client/index.js +2 -2
- package/dist/lib/crypto/file.js +12 -12
- package/dist/lib/index.js +0 -3
- package/dist/lib/utils.js +2 -0
- package/dist/types/base-client.d.ts +1 -1
- package/dist/types/cache.d.ts +2 -0
- package/dist/types/crypto/file.d.ts +3 -3
- package/dist/types/index.d.ts +0 -2
- package/dist/types/utils.d.ts +2 -0
- package/package.json +2 -1
package/dist/lib/base-client.js
CHANGED
|
@@ -58,7 +58,7 @@ export class BaseClient {
|
|
|
58
58
|
}
|
|
59
59
|
async updateProfile(data) {
|
|
60
60
|
const updateProfile = await this.client.user.updateProfile.mutate(data);
|
|
61
|
-
return updateProfile;
|
|
61
|
+
return updateProfile;
|
|
62
62
|
}
|
|
63
63
|
static async isCryptoTransactionDone({ idOrHash, network = 'mainnet', }) {
|
|
64
64
|
const { isDone } = await this.getBaseClient().crypto.isTransactionDone.query({
|
package/dist/lib/cache.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
import { LRUCache } from 'lru-cache';
|
|
2
|
+
import { gigaToBytes } from './utils.js';
|
|
1
3
|
export const filesCache = new Map();
|
|
2
4
|
export const nodesCache = new Map();
|
|
3
5
|
export const usersCache = new Map();
|
|
4
6
|
export const publicKeysCache = new Map();
|
|
7
|
+
export const dataCache = new LRUCache({
|
|
8
|
+
max: 500,
|
|
9
|
+
maxSize: gigaToBytes(0.5),
|
|
10
|
+
sizeCalculation: (value) => {
|
|
11
|
+
return value.byteLength;
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import ky from 'ky';
|
|
3
3
|
import { encryptName } from '../index.js';
|
|
4
|
-
import { nodesCache, filesCache } from '../cache.js';
|
|
5
|
-
import {
|
|
4
|
+
import { nodesCache, filesCache, dataCache } from '../cache.js';
|
|
5
|
+
import { secretStreamKeygen } from '../crypto/file.js';
|
|
6
6
|
import { decryptCryptoBox, encryptCryptoBox } from '../crypto/index.js';
|
|
7
7
|
import { compress, decompress } from '../minify/index.js';
|
|
8
8
|
import { sodium } from '../sodium.js';
|
|
@@ -35,16 +35,19 @@ export class SecrecyCloudClient {
|
|
|
35
35
|
await this.#apiClient.cloud.shareFileInHistory.mutate({
|
|
36
36
|
fileId: file.id,
|
|
37
37
|
nodeId,
|
|
38
|
-
users: users.map(([u]) =>
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
users: await Promise.all(users.map(async ([u]) => {
|
|
39
|
+
const userPubKey = await this.#client.app.userPublicKey(u.id);
|
|
40
|
+
return {
|
|
41
|
+
id: u.id,
|
|
42
|
+
key: sodium.to_hex(encryptCryptoBox(sodium.from_hex(file.key), userPubKey, this.#keys.privateKey)),
|
|
43
|
+
};
|
|
41
44
|
})),
|
|
42
45
|
});
|
|
43
46
|
}
|
|
44
47
|
return internalNodeFullToNodeFull(node);
|
|
45
48
|
}
|
|
46
49
|
async uploadFile({ file, encryptProgress, uploadProgress, signal, }) {
|
|
47
|
-
const fileKey =
|
|
50
|
+
const fileKey = secretStreamKeygen();
|
|
48
51
|
const fileBuffer = file instanceof File ? new Uint8Array(await file.arrayBuffer()) : file;
|
|
49
52
|
const compressed = compress(fileBuffer);
|
|
50
53
|
const { data: encryptedFile, md5: md5File, md5Encrypted, } = await encrypt(fileKey, compressed, encryptProgress, signal);
|
|
@@ -118,16 +121,12 @@ export class SecrecyCloudClient {
|
|
|
118
121
|
signal,
|
|
119
122
|
});
|
|
120
123
|
await uploadPartEnded(chunk.md5, chunk.order);
|
|
121
|
-
// if ((e as any).response.status === 0) {
|
|
122
|
-
// // TODO https://github.com/sindresorhus/ky/issues/305
|
|
123
|
-
// } else {
|
|
124
|
-
// throw e;
|
|
125
|
-
// }
|
|
126
124
|
};
|
|
127
125
|
await promiseAllLimit(3, uploadFile.parts.map((p) => async () => {
|
|
128
126
|
await byPart(p);
|
|
129
127
|
}));
|
|
130
128
|
await uploadEnded();
|
|
129
|
+
dataCache.set(uploadFile.fileId, fileBuffer);
|
|
131
130
|
return uploadFile.fileId;
|
|
132
131
|
}
|
|
133
132
|
async uploadFileInCloud({ file, name, nodeId, encryptProgress, uploadProgress, signal, }) {
|
|
@@ -194,7 +193,7 @@ export class SecrecyCloudClient {
|
|
|
194
193
|
return isDeleted;
|
|
195
194
|
}
|
|
196
195
|
async createFolder({ name, parentFolderId, }) {
|
|
197
|
-
const key =
|
|
196
|
+
const key = secretStreamKeygen();
|
|
198
197
|
const encryptedName = await encryptName(name, sodium.to_hex(key));
|
|
199
198
|
const encryptedKey = encryptCryptoBox(key, this.#keys.publicKey, this.#keys.privateKey);
|
|
200
199
|
const createFolder = await this.#apiClient.cloud.createFolder.mutate({
|
|
@@ -272,6 +271,10 @@ export class SecrecyCloudClient {
|
|
|
272
271
|
return await apiNodeToExternalNodeFull(updateNode, this.#keys);
|
|
273
272
|
}
|
|
274
273
|
async fileContent({ fileId, onDownloadProgress, progressDecrypt, signal, }) {
|
|
274
|
+
const cached = dataCache.get(fileId);
|
|
275
|
+
if (cached !== undefined) {
|
|
276
|
+
return cached;
|
|
277
|
+
}
|
|
275
278
|
const fileContent = await this.#apiClient.cloud.fileContentById.query({
|
|
276
279
|
id: fileId,
|
|
277
280
|
});
|
|
@@ -344,7 +347,9 @@ export class SecrecyCloudClient {
|
|
|
344
347
|
if (encryptedContent === null) {
|
|
345
348
|
throw `Can't find content for file ${fileId}`;
|
|
346
349
|
}
|
|
347
|
-
|
|
350
|
+
const data = await finalize(encryptedContent);
|
|
351
|
+
dataCache.set(fileId, data);
|
|
352
|
+
return data;
|
|
348
353
|
}
|
|
349
354
|
async deleteFile({ fileId, nodeId, }) {
|
|
350
355
|
const { isDeleted } = await this.#apiClient.cloud.deleteFile.mutate({
|
|
@@ -425,7 +430,7 @@ export class SecrecyCloudClient {
|
|
|
425
430
|
key = file.key;
|
|
426
431
|
}
|
|
427
432
|
key = sodium.to_hex(encryptCryptoBox(sodium.from_hex(key), this.#keys.publicKey, this.#keys.privateKey));
|
|
428
|
-
const nameKey =
|
|
433
|
+
const nameKey = secretStreamKeygen();
|
|
429
434
|
const encryptedName = await encryptName(name, sodium.to_hex(nameKey));
|
|
430
435
|
const encryptedNameKey = sodium.to_hex(encryptCryptoBox(nameKey, this.#keys.publicKey, this.#keys.privateKey));
|
|
431
436
|
const saveInCloud = await this.#apiClient.cloud.saveInCloud.mutate({
|
|
@@ -123,7 +123,7 @@ export class SecrecyMailClient {
|
|
|
123
123
|
return false;
|
|
124
124
|
}
|
|
125
125
|
const temporaryRecipients = new Array();
|
|
126
|
-
const recipients = new Array();
|
|
126
|
+
const recipients = new Array();
|
|
127
127
|
for (const { email } of draft.temporaryRecipients) {
|
|
128
128
|
if (email === undefined) {
|
|
129
129
|
continue;
|
|
@@ -154,7 +154,7 @@ export class SecrecyMailClient {
|
|
|
154
154
|
return isSent;
|
|
155
155
|
}
|
|
156
156
|
async sendWaitingEmails() {
|
|
157
|
-
// TODO
|
|
157
|
+
// TODO optimize this
|
|
158
158
|
const mails = await this.sentMails();
|
|
159
159
|
const filtered = mails.filter((m) => m.temporaryRecipients.length > 0);
|
|
160
160
|
for (const mail of filtered) {
|
|
@@ -196,8 +196,7 @@ export class SecrecyMailClient {
|
|
|
196
196
|
});
|
|
197
197
|
}
|
|
198
198
|
const createDraftMail = await this.#apiClient.mail.createDraft.mutate({
|
|
199
|
-
|
|
200
|
-
recipients, // TODO
|
|
199
|
+
recipients,
|
|
201
200
|
replyToId,
|
|
202
201
|
body: sodium.to_hex(encryptCryptoBox(sodium.from_string(body), this.#keys.publicKey, this.#keys.privateKey)),
|
|
203
202
|
subject: sodium.to_hex(encryptCryptoBox(sodium.from_string(subject), this.#keys.publicKey, this.#keys.privateKey)),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { sodium } from '../../sodium.js';
|
|
2
2
|
import { decryptCryptoBox } from '../../crypto/index.js';
|
|
3
3
|
import { nodesCache } from '../../cache.js';
|
|
4
|
-
import {
|
|
4
|
+
import { decryptSecretStream } from '../../crypto/file.js';
|
|
5
5
|
import { apiFileToInternal, internalFileToFile } from './file.js';
|
|
6
6
|
export async function apiNodeToInternal(apiNode, keyPair) {
|
|
7
7
|
const internal = {
|
|
@@ -28,7 +28,7 @@ export async function apiNodeToInternal(apiNode, keyPair) {
|
|
|
28
28
|
internal.access = { ...apiNode.access };
|
|
29
29
|
if (apiNode.access.nameKey !== null) {
|
|
30
30
|
const key = decryptCryptoBox(sodium.from_hex(apiNode.access.nameKey), apiNode.access.sharedByPubKey, keyPair.privateKey);
|
|
31
|
-
internal.name = sodium.to_string(await
|
|
31
|
+
internal.name = sodium.to_string(await decryptSecretStream(key, sodium.from_hex(internal.name)));
|
|
32
32
|
internal.access.nameKey = sodium.to_hex(key);
|
|
33
33
|
}
|
|
34
34
|
for (const b of internal.breadcrumb) {
|
|
@@ -37,7 +37,7 @@ export async function apiNodeToInternal(apiNode, keyPair) {
|
|
|
37
37
|
}
|
|
38
38
|
const key = decryptCryptoBox(sodium.from_hex(b.nameKey), b.pubKey, keyPair.privateKey);
|
|
39
39
|
b.nameKey = sodium.to_hex(key);
|
|
40
|
-
b.name = sodium.to_string(await
|
|
40
|
+
b.name = sodium.to_string(await decryptSecretStream(key, sodium.from_hex(b.name)));
|
|
41
41
|
}
|
|
42
42
|
nodesCache.set(internal.id, internal);
|
|
43
43
|
return internal;
|
package/dist/lib/client/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseClient } from '../base-client.js';
|
|
2
|
-
import {
|
|
2
|
+
import { encryptSecretStream } from '../crypto/file.js';
|
|
3
3
|
import { sodium } from '../sodium.js';
|
|
4
4
|
import { SecrecyCloudClient } from './SecrecyCloudClient.js';
|
|
5
5
|
import { SecrecyMailClient } from './SecrecyMailClient.js';
|
|
@@ -11,7 +11,7 @@ import { SecrecyPayClient } from './SecrecyPayClient.js';
|
|
|
11
11
|
import { SecrecyUserClient } from './SecrecyUserClient.js';
|
|
12
12
|
import { SecrecyCareClient } from './SecrecryCareClient.js';
|
|
13
13
|
export const encryptName = async (name, nameKey) => {
|
|
14
|
-
const { data } = await
|
|
14
|
+
const { data } = await encryptSecretStream(sodium.from_hex(nameKey), sodium.from_string(name));
|
|
15
15
|
const nameEncrypted = sodium.to_hex(data);
|
|
16
16
|
return nameEncrypted;
|
|
17
17
|
};
|
package/dist/lib/crypto/file.js
CHANGED
|
@@ -6,7 +6,7 @@ function assert(c, message) {
|
|
|
6
6
|
throw new Error(message);
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
export function
|
|
9
|
+
export function secretStreamKeygen() {
|
|
10
10
|
return sodium.crypto_secretstream_xchacha20poly1305_keygen();
|
|
11
11
|
}
|
|
12
12
|
function encrypt(key) {
|
|
@@ -46,7 +46,7 @@ function decrypt(header, key) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
export const CHUNK_SIZE = 8192;
|
|
49
|
-
export async function
|
|
49
|
+
export async function encryptSecretStream(key, data, progress, abort) {
|
|
50
50
|
await setup();
|
|
51
51
|
const { encrypt: crypt, destroy, header } = encrypt(key);
|
|
52
52
|
const encryptedChunk = CHUNK_SIZE + sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
|
|
@@ -102,11 +102,11 @@ export async function encryptSecretstream(key, data, progress, abort) {
|
|
|
102
102
|
md5: spark.end(),
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
|
-
export async function
|
|
105
|
+
export async function decryptSecretStream(key, data, progress, abort) {
|
|
106
106
|
await setup();
|
|
107
107
|
const header = data.slice(0, sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES);
|
|
108
108
|
data = data.slice(sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES);
|
|
109
|
-
const { decrypt:
|
|
109
|
+
const { decrypt: decryptFn, destroy } = decrypt(header, key);
|
|
110
110
|
const chunkSize = CHUNK_SIZE + sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
|
|
111
111
|
const max = Math.ceil(data.byteLength / chunkSize) * CHUNK_SIZE;
|
|
112
112
|
void progress?.({
|
|
@@ -121,9 +121,9 @@ export async function decryptSecretstream(key, data, progress, abort) {
|
|
|
121
121
|
if (abort?.signal.aborted === true) {
|
|
122
122
|
throw new Error(`Decrypt aborted`);
|
|
123
123
|
}
|
|
124
|
-
const
|
|
125
|
-
final.set(
|
|
126
|
-
total +=
|
|
124
|
+
const messageTag = decryptFn(chunk);
|
|
125
|
+
final.set(messageTag.message, total);
|
|
126
|
+
total += messageTag.message.byteLength;
|
|
127
127
|
const percent = total / max;
|
|
128
128
|
if (percent > lastPercent + 0.01) {
|
|
129
129
|
void progress?.({
|
|
@@ -142,13 +142,13 @@ export async function decryptSecretstream(key, data, progress, abort) {
|
|
|
142
142
|
});
|
|
143
143
|
return final.slice(0, total);
|
|
144
144
|
}
|
|
145
|
-
// async function
|
|
145
|
+
// async function mainSecretStream(random: Uint8Array): Promise<vo id> {
|
|
146
146
|
// const key = secretstreamKeygen();
|
|
147
147
|
// console.time("secretstream_encrypt");
|
|
148
|
-
// const crypted = en
|
|
148
|
+
// const crypted = en cryptSecretStream(key, random);
|
|
149
149
|
// consol e.timeEnd("secretstream_encrypt");
|
|
150
150
|
// console.time("secretstream_decrypt");
|
|
151
|
-
// const decrypted =
|
|
151
|
+
// const decrypted = decryptSecretStream(key, crypted);
|
|
152
152
|
// con sole.timeEnd("secretstream_decrypt");
|
|
153
153
|
// const first = t o_hex(random).slice(0, 32);
|
|
154
154
|
// const final = to_hex(decrypted).slice(0, 32);
|
|
@@ -158,7 +158,7 @@ export async function decryptSecretstream(key, data, progress, abort) {
|
|
|
158
158
|
// equals: firs t === final
|
|
159
159
|
// }) ;
|
|
160
160
|
// }
|
|
161
|
-
// async function
|
|
161
|
+
// async function mainSecretBox(random: Uint8Array): Promise< void> {
|
|
162
162
|
// const key = generateSecretBox();
|
|
163
163
|
// console.time("secretbox _encrypt");
|
|
164
164
|
// const crypted = encryptFile(random, key);
|
|
@@ -179,6 +179,6 @@ export async function decryptSecretstream(key, data, progress, abort) {
|
|
|
179
179
|
// console.time("randombytes_buf");
|
|
180
180
|
// const random = randombytes_buf(1_000_000 * 1024);
|
|
181
181
|
// consol e.timeEnd("randombytes_buf");
|
|
182
|
-
// await Promise.all([
|
|
182
|
+
// await Promise.all([mainSecretStream(random), mainSecretBox(random)]);
|
|
183
183
|
// }
|
|
184
184
|
// main();
|
package/dist/lib/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export declare class BaseClient {
|
|
|
12
12
|
static getUser(userId: string, sessionId?: string | null | undefined): Promise<PublicUser>;
|
|
13
13
|
getUser(userId: string): Promise<PublicUser>;
|
|
14
14
|
searchUsers(search: string): Promise<PublicUser[]>;
|
|
15
|
-
updateProfile(data: RouterInputs['user']['updateProfile']): Promise<SelfUser
|
|
15
|
+
updateProfile(data: RouterInputs['user']['updateProfile']): Promise<Omit<SelfUser, 'account'>>;
|
|
16
16
|
static isCryptoTransactionDone({ idOrHash, network, }: {
|
|
17
17
|
idOrHash: string;
|
|
18
18
|
network?: InfuraNetwork;
|
package/dist/types/cache.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { InternalNode, InternalFile, InternalNodeFull } from './client/types/index.js';
|
|
2
|
+
import { LRUCache } from 'lru-cache';
|
|
2
3
|
export declare const filesCache: Map<string, InternalFile>;
|
|
3
4
|
export declare const nodesCache: Map<string, InternalNode | InternalNodeFull>;
|
|
4
5
|
export declare const usersCache: Map<string, {
|
|
@@ -9,3 +10,4 @@ export declare const usersCache: Map<string, {
|
|
|
9
10
|
isSearchable: boolean;
|
|
10
11
|
}>;
|
|
11
12
|
export declare const publicKeysCache: Map<string, string>;
|
|
13
|
+
export declare const dataCache: LRUCache<string, Uint8Array, unknown>;
|
|
@@ -3,12 +3,12 @@ export interface EncryptedFile {
|
|
|
3
3
|
md5: string;
|
|
4
4
|
md5Encrypted: string;
|
|
5
5
|
}
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function secretStreamKeygen(): Uint8Array;
|
|
7
7
|
export declare const CHUNK_SIZE = 8192;
|
|
8
8
|
export interface Progress {
|
|
9
9
|
percent: number;
|
|
10
10
|
total: number;
|
|
11
11
|
current: number;
|
|
12
12
|
}
|
|
13
|
-
export declare function
|
|
14
|
-
export declare function
|
|
13
|
+
export declare function encryptSecretStream(key: Uint8Array, data: Uint8Array, progress?: (progress: Progress) => Promise<void>, abort?: AbortController): Promise<EncryptedFile>;
|
|
14
|
+
export declare function decryptSecretStream(key: Uint8Array, data: Uint8Array, progress?: (progress: Progress) => Promise<void>, abort?: AbortController): Promise<Uint8Array>;
|
package/dist/types/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@secrecy/lib",
|
|
3
3
|
"author": "Anonymize <anonymize@gmail.com>",
|
|
4
4
|
"description": "Anonymize Secrecy Library",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.11.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/anonymize-org/lib.git"
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
"jsonwebtoken": "^9.0.2",
|
|
85
85
|
"ky": "^1.2.3",
|
|
86
86
|
"libsodium-wrappers-sumo": "^0.7.13",
|
|
87
|
+
"lru-cache": "^10.2.0",
|
|
87
88
|
"spark-md5": "^3.0.2",
|
|
88
89
|
"superjson": "2.2.1",
|
|
89
90
|
"zod": "3.22.4"
|