Skip to content

Commit 7e355c3

Browse files
committed
crypto: @notesnook/sodium no longer requires top-level await
1 parent 6946848 commit 7e355c3

File tree

11 files changed

+441
-297
lines changed

11 files changed

+441
-297
lines changed

packages/crypto/src/decryption.ts

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,29 @@ You should have received a copy of the GNU General Public License
1717
along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919

20-
import {
21-
crypto_aead_xchacha20poly1305_ietf_decrypt,
22-
crypto_secretstream_xchacha20poly1305_init_pull,
23-
crypto_secretstream_xchacha20poly1305_pull,
24-
to_base64,
25-
from_base64,
26-
base64_variants,
27-
to_string,
28-
crypto_secretstream_xchacha20poly1305_TAG_FINAL,
29-
from_hex
30-
} from "@notesnook/sodium";
20+
import { base64_variants, ISodium } from "@notesnook/sodium";
3121
import KeyUtils from "./keyutils.js";
3222
import { Cipher, Output, DataFormat, SerializedKey } from "./types.js";
3323

3424
export default class Decryption {
35-
private static transformInput(cipherData: Cipher<DataFormat>): Uint8Array {
25+
private static transformInput(
26+
sodium: ISodium,
27+
cipherData: Cipher<DataFormat>
28+
): Uint8Array {
3629
let input: Uint8Array | null = null;
3730
if (
3831
typeof cipherData.cipher === "string" &&
3932
cipherData.format === "base64"
4033
) {
41-
input = from_base64(
34+
input = sodium.from_base64(
4235
cipherData.cipher,
4336
base64_variants.URLSAFE_NO_PADDING
4437
);
4538
} else if (
4639
typeof cipherData.cipher === "string" &&
4740
cipherData.format === "hex"
4841
) {
49-
input = from_hex(cipherData.cipher);
42+
input = sodium.from_hex(cipherData.cipher);
5043
} else if (cipherData.cipher instanceof Uint8Array) {
5144
input = cipherData.cipher;
5245
}
@@ -55,52 +48,51 @@ export default class Decryption {
5548
}
5649

5750
static decrypt<TOutputFormat extends DataFormat>(
51+
sodium: ISodium,
5852
key: SerializedKey,
5953
cipherData: Cipher<DataFormat>,
6054
outputFormat: TOutputFormat = "text" as TOutputFormat
6155
): Output<TOutputFormat> {
6256
if (!key.salt && cipherData.salt) key.salt = cipherData.salt;
63-
const encryptionKey = KeyUtils.transform(key);
57+
const encryptionKey = KeyUtils.transform(sodium, key);
6458

65-
const input = this.transformInput(cipherData);
66-
const plaintext = crypto_aead_xchacha20poly1305_ietf_decrypt(
59+
const input = this.transformInput(sodium, cipherData);
60+
const plaintext = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
6761
null,
6862
input,
6963
null,
70-
from_base64(cipherData.iv),
64+
sodium.from_base64(cipherData.iv),
7165
encryptionKey.key
7266
);
7367

7468
return (
7569
outputFormat === "base64"
76-
? to_base64(plaintext, base64_variants.ORIGINAL)
70+
? sodium.to_base64(plaintext, base64_variants.ORIGINAL)
7771
: outputFormat === "text"
78-
? to_string(plaintext)
72+
? sodium.to_string(plaintext)
7973
: plaintext
8074
) as Output<TOutputFormat>;
8175
}
8276

8377
static createStream(
78+
sodium: ISodium,
8479
header: string,
8580
key: SerializedKey
8681
): TransformStream<Uint8Array, Uint8Array> {
87-
const { key: _key } = KeyUtils.transform(key);
88-
const state = crypto_secretstream_xchacha20poly1305_init_pull(
89-
from_base64(header),
82+
const { key: _key } = KeyUtils.transform(sodium, key);
83+
const state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
84+
sodium.from_base64(header),
9085
_key
9186
);
9287

9388
return new TransformStream<Uint8Array, Uint8Array>({
9489
start() {},
9590
transform(chunk, controller) {
96-
const { message, tag } = crypto_secretstream_xchacha20poly1305_pull(
97-
state,
98-
chunk,
99-
null
100-
);
91+
const { message, tag } =
92+
sodium.crypto_secretstream_xchacha20poly1305_pull(state, chunk, null);
10193
if (!message) throw new Error("Could not decrypt chunk.");
10294
controller.enqueue(message);
103-
if (tag === crypto_secretstream_xchacha20poly1305_TAG_FINAL)
95+
if (tag === sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL)
10496
controller.terminate();
10597
}
10698
});

packages/crypto/src/encryption.ts

Lines changed: 22 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,20 @@ You should have received a copy of the GNU General Public License
1717
along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919

20-
import {
21-
crypto_aead_xchacha20poly1305_ietf_encrypt,
22-
crypto_secretstream_xchacha20poly1305_init_push,
23-
crypto_secretstream_xchacha20poly1305_push,
24-
randombytes_buf,
25-
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
26-
crypto_secretstream_xchacha20poly1305_TAG_FINAL,
27-
crypto_secretstream_xchacha20poly1305_TAG_MESSAGE,
28-
to_base64,
29-
from_base64,
30-
base64_variants
31-
} from "@notesnook/sodium";
20+
import { ISodium, base64_variants } from "@notesnook/sodium";
3221
import KeyUtils from "./keyutils.js";
3322
import { Chunk, Cipher, Input, DataFormat, SerializedKey } from "./types.js";
3423

3524
const encoder = new TextEncoder();
3625
export default class Encryption {
3726
private static transformInput(
27+
sodium: ISodium,
3828
input: Input<DataFormat>,
3929
format: DataFormat
4030
): Uint8Array {
4131
let data: Uint8Array | null = null;
4232
if (typeof input === "string" && format === "base64") {
43-
data = from_base64(input, base64_variants.ORIGINAL);
33+
data = sodium.from_base64(input, base64_variants.ORIGINAL);
4434
} else if (typeof input === "string") {
4535
data = encoder.encode(input);
4636
} else if (input instanceof Uint8Array) {
@@ -51,18 +41,21 @@ export default class Encryption {
5141
}
5242

5343
static encrypt<TOutputFormat extends DataFormat>(
44+
sodium: ISodium,
5445
key: SerializedKey,
5546
input: Input<DataFormat>,
5647
format: DataFormat,
5748
outputFormat: TOutputFormat = "uint8array" as TOutputFormat
5849
): Cipher<TOutputFormat> {
59-
const encryptionKey = KeyUtils.transform(key);
60-
const data = this.transformInput(input, format);
50+
const encryptionKey = KeyUtils.transform(sodium, key);
51+
const data = this.transformInput(sodium, input, format);
6152

62-
const nonce = randombytes_buf(crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
53+
const nonce = sodium.randombytes_buf(
54+
sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
55+
);
6356

6457
const cipher: string | Uint8Array =
65-
crypto_aead_xchacha20poly1305_ietf_encrypt(
58+
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
6659
data,
6760
null,
6861
null,
@@ -72,10 +65,10 @@ export default class Encryption {
7265

7366
let output: string | Uint8Array = cipher;
7467
if (outputFormat === "base64") {
75-
output = to_base64(cipher, base64_variants.URLSAFE_NO_PADDING);
68+
output = sodium.to_base64(cipher, base64_variants.URLSAFE_NO_PADDING);
7669
}
7770

78-
const iv = to_base64(nonce);
71+
const iv = sodium.to_base64(nonce);
7972
return {
8073
format: outputFormat,
8174
alg: getAlgorithm(base64_variants.URLSAFE_NO_PADDING),
@@ -86,29 +79,30 @@ export default class Encryption {
8679
} as Cipher<TOutputFormat>;
8780
}
8881

89-
static createStream(key: SerializedKey): {
82+
static createStream(
83+
sodium: ISodium,
84+
key: SerializedKey
85+
): {
9086
iv: string;
9187
stream: TransformStream<Chunk, Uint8Array>;
9288
} {
93-
const { key: _key } = KeyUtils.transform(key);
94-
const { state, header } = crypto_secretstream_xchacha20poly1305_init_push(
95-
_key,
96-
"base64"
97-
);
89+
const { key: _key } = KeyUtils.transform(sodium, key);
90+
const { state, header } =
91+
sodium.crypto_secretstream_xchacha20poly1305_init_push(_key, "base64");
9892

9993
return {
10094
iv: header,
10195
stream: new TransformStream<Chunk, Uint8Array>({
10296
start() {},
10397
transform(chunk, controller) {
10498
controller.enqueue(
105-
crypto_secretstream_xchacha20poly1305_push(
99+
sodium.crypto_secretstream_xchacha20poly1305_push(
106100
state,
107101
chunk.data,
108102
null,
109103
chunk.final
110-
? crypto_secretstream_xchacha20poly1305_TAG_FINAL
111-
: crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
104+
? sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
105+
: sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
112106
)
113107
);
114108
if (chunk.final) controller.terminate();
@@ -118,30 +112,6 @@ export default class Encryption {
118112
}
119113
}
120114

121-
// class EncryptionStream {
122-
// state: StateAddress;
123-
// header: string;
124-
// constructor(key: EncryptionKey) {
125-
// const { state, header } = crypto_secretstream_xchacha20poly1305_init_push(
126-
// key.key,
127-
// "base64"
128-
// );
129-
// this.state = state;
130-
// this.header = header;
131-
// }
132-
133-
// write(chunk: Uint8Array, final?: boolean): Uint8Array {
134-
// return crypto_secretstream_xchacha20poly1305_push(
135-
// this.state,
136-
// chunk,
137-
// null,
138-
// final
139-
// ? crypto_secretstream_xchacha20poly1305_TAG_FINAL
140-
// : crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
141-
// );
142-
// }
143-
// }
144-
145115
function getAlgorithm(base64Variant: base64_variants) {
146116
//Template: encryptionAlgorithm-kdfAlgorithm-base64variant
147117
return `xcha-argon2i13-${base64Variant}`;

packages/crypto/src/index.ts

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
1717
along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919

20-
import { initialize } from "@notesnook/sodium";
20+
import { ISodium, Sodium } from "@notesnook/sodium";
2121
import Decryption from "./decryption.js";
2222
import Encryption from "./encryption.js";
2323
import { INNCrypto } from "./interfaces.js";
@@ -34,10 +34,11 @@ import {
3434

3535
export class NNCrypto implements INNCrypto {
3636
private isReady = false;
37+
private sodium: ISodium = new Sodium();
3738

3839
private async init() {
3940
if (this.isReady) return;
40-
await initialize();
41+
await this.sodium.initialize();
4142
this.isReady = true;
4243
}
4344

@@ -49,6 +50,7 @@ export class NNCrypto implements INNCrypto {
4950
): Promise<Cipher<TOutputFormat>> {
5051
await this.init();
5152
return Encryption.encrypt(
53+
this.sodium,
5254
key,
5355
input,
5456
format,
@@ -64,7 +66,7 @@ export class NNCrypto implements INNCrypto {
6466
): Promise<Cipher<TOutputFormat>[]> {
6567
await this.init();
6668
return items.map((data) =>
67-
Encryption.encrypt(key, data, format, outputFormat)
69+
Encryption.encrypt(this.sodium, key, data, format, outputFormat)
6870
);
6971
}
7072

@@ -74,7 +76,7 @@ export class NNCrypto implements INNCrypto {
7476
outputFormat: TOutputFormat = "text" as TOutputFormat
7577
): Promise<Output<TOutputFormat>> {
7678
await this.init();
77-
return Decryption.decrypt(key, cipherData, outputFormat);
79+
return Decryption.decrypt(this.sodium, key, cipherData, outputFormat);
7880
}
7981

8082
async decryptMulti<TOutputFormat extends DataFormat>(
@@ -85,88 +87,37 @@ export class NNCrypto implements INNCrypto {
8587
await this.init();
8688
const decryptedItems: Output<TOutputFormat>[] = [];
8789
for (const cipherData of items) {
88-
decryptedItems.push(Decryption.decrypt(key, cipherData, outputFormat));
90+
decryptedItems.push(
91+
Decryption.decrypt(this.sodium, key, cipherData, outputFormat)
92+
);
8993
}
9094
return decryptedItems;
9195
}
9296

9397
async hash(password: string, salt: string): Promise<string> {
9498
await this.init();
95-
return Password.hash(password, salt);
99+
return Password.hash(this.sodium, password, salt);
96100
}
97101

98102
async deriveKey(password: string, salt?: string): Promise<EncryptionKey> {
99103
await this.init();
100-
return KeyUtils.deriveKey(password, salt);
104+
return KeyUtils.deriveKey(this.sodium, password, salt);
101105
}
102106

103107
async exportKey(password: string, salt?: string): Promise<SerializedKey> {
104108
await this.init();
105-
return KeyUtils.exportKey(password, salt);
109+
return KeyUtils.exportKey(this.sodium, password, salt);
106110
}
107111

108112
async createEncryptionStream(key: SerializedKey) {
109113
await this.init();
110-
return Encryption.createStream(key);
111-
112-
// // eslint-disable-next-line no-constant-condition
113-
// while (true) {
114-
// const chunk = await stream.read();
115-
// if (!chunk) break;
116-
117-
// const { data, final } = chunk;
118-
// if (!data) break;
119-
120-
// const encryptedChunk: Chunk = {
121-
// data: encryptionStream.write(data, final),
122-
// final
123-
// };
124-
// await stream.write(encryptedChunk);
125-
126-
// if (final) break;
127-
// }
128-
// return encryptionStream.header;
114+
return Encryption.createStream(this.sodium, key);
129115
}
130116

131117
async createDecryptionStream(key: SerializedKey, iv: string) {
132118
await this.init();
133-
return Decryption.createStream(iv, key);
134-
// eslint-disable-next-line no-constant-condition
135-
// while (true) {
136-
// const chunk = await stream.read();
137-
// if (!chunk) break;
138-
139-
// const { data, final } = chunk;
140-
// if (!data) break;
141-
142-
// const decryptedChunk: Chunk = {
143-
// data: decryptionStream.read(data),
144-
// final
145-
// };
146-
// await stream.write(decryptedChunk);
147-
148-
// if (final) break;
149-
// }
119+
return Decryption.createStream(this.sodium, iv, key);
150120
}
151-
152-
// async encryptStream(
153-
// key: SerializedKey,
154-
// stream: IStreamable,
155-
// _streamId?: string
156-
// ): Promise<string> {
157-
// await this.init();
158-
// return await this.createEncryptionStream(key, stream);
159-
// }
160-
161-
// async decryptStream(
162-
// key: SerializedKey,
163-
// iv: string,
164-
// stream: IStreamable,
165-
// _streamId?: string
166-
// ): Promise<void> {
167-
// await this.init();
168-
// await this.createDecryptionStream(iv, key, stream);
169-
// }
170121
}
171122

172123
export * from "./types.js";

0 commit comments

Comments
 (0)