@cloudpss/crypto 0.6.15 → 0.6.16
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/README.md +10 -0
- package/dist/encryption/browser.d.ts +2 -0
- package/dist/encryption/browser.d.ts.map +1 -1
- package/dist/encryption/browser.js +14 -2
- package/dist/encryption/browser.js.map +1 -1
- package/dist/encryption/index.d.ts +12 -6
- package/dist/encryption/index.d.ts.map +1 -1
- package/dist/encryption/index.js +16 -6
- package/dist/encryption/index.js.map +1 -1
- package/dist/encryption/module.d.ts +3 -7
- package/dist/encryption/module.d.ts.map +1 -1
- package/dist/encryption/module.js +3 -8
- package/dist/encryption/module.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/encryption/browser.ts +17 -2
- package/src/encryption/index.ts +20 -9
- package/src/encryption/module.ts +6 -15
- package/src/index.ts +1 -1
- package/tests/.check.ts +134 -0
- package/tests/browser.ts +28 -0
- package/tests/encryption.ts +36 -166
- package/tests/modules.ts +30 -0
package/README.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
# @cloudpss/crypto
|
|
2
2
|
|
|
3
3
|
Pre-configured crypto libraries for use in CloudPSS.
|
|
4
|
+
|
|
5
|
+
## Example
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { encrypt, decrypt, isEncrypted } from '@cloudpss/crypto';
|
|
9
|
+
|
|
10
|
+
const encrypted = await encrypt(new TextEncoder().encode('secret'), 'passphrase');
|
|
11
|
+
const ok = isEncrypted(encrypted);
|
|
12
|
+
const plain = await decrypt(encrypted, 'passphrase');
|
|
13
|
+
```
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { EncryptedData, PlainData } from './common.js';
|
|
2
|
+
/** For test purposes */
|
|
3
|
+
export declare function resetModuleLoader(): void;
|
|
2
4
|
/** browser encrypt */
|
|
3
5
|
export declare function encrypt(data: PlainData, passphrase: string): Promise<EncryptedData>;
|
|
4
6
|
/** browser decrypt */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/encryption/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/encryption/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAoB5D,wBAAwB;AACxB,wBAAgB,iBAAiB,IAAI,IAAI,CAOxC;AAID,sBAAsB;AACtB,wBAAsB,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAGzF;AAED,sBAAsB;AACtB,wBAAsB,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAGzF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/** 动态选择加密模块 */
|
|
2
|
+
async function loadModule() {
|
|
2
3
|
if (typeof crypto == 'object' &&
|
|
3
4
|
typeof crypto.subtle == 'object' &&
|
|
4
5
|
typeof crypto.subtle.importKey == 'function' &&
|
|
@@ -10,7 +11,18 @@ const module = async () => {
|
|
|
10
11
|
else {
|
|
11
12
|
return import('./wasm.js');
|
|
12
13
|
}
|
|
13
|
-
}
|
|
14
|
+
}
|
|
15
|
+
let module;
|
|
16
|
+
/** For test purposes */
|
|
17
|
+
export function resetModuleLoader() {
|
|
18
|
+
module = async () => {
|
|
19
|
+
const mod = await loadModule();
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
21
|
+
module = () => Promise.resolve(mod);
|
|
22
|
+
return mod;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
resetModuleLoader();
|
|
14
26
|
/** browser encrypt */
|
|
15
27
|
export async function encrypt(data, passphrase) {
|
|
16
28
|
const { encrypt } = await module();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/encryption/browser.ts"],"names":[],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/encryption/browser.ts"],"names":[],"mappings":"AAEA,eAAe;AACf,KAAK,UAAU,UAAU;IACrB,IACI,OAAO,MAAM,IAAI,QAAQ;QACzB,OAAO,MAAM,CAAC,MAAM,IAAI,QAAQ;QAChC,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,UAAU;QAC5C,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,UAAU;QAC5C,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,UAAU;QAC1C,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,UAAU,EAC5C,CAAC;QACC,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACJ,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;AACL,CAAC;AAED,IAAI,MAAyB,CAAC;AAE9B,wBAAwB;AACxB,MAAM,UAAU,iBAAiB;IAC7B,MAAM,GAAG,KAAK,IAAI,EAAE;QAChB,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;QAC/B,qEAAqE;QACrE,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,OAAO,GAAG,CAAC;IACf,CAAC,CAAC;AACN,CAAC;AAED,iBAAiB,EAAE,CAAC;AAEpB,sBAAsB;AACtB,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAe,EAAE,UAAkB;IAC7D,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,EAAE,CAAC;IACnC,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAC3C,CAAC;AAED,sBAAsB;AACtB,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAmB,EAAE,UAAkB;IACjE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,EAAE,CAAC;IACnC,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -5,22 +5,28 @@ export declare function isEncrypted(data: BufferSource): boolean;
|
|
|
5
5
|
* 从加密数据中提取附加数据
|
|
6
6
|
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
7
7
|
*/
|
|
8
|
-
export declare function extractAad(data: BufferSource): Uint8Array | undefined;
|
|
8
|
+
export declare function extractAad(data: BufferSource): Uint8Array<ArrayBuffer> | undefined;
|
|
9
9
|
export declare const
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* 加密数据,包含不加密的附加数据
|
|
12
12
|
* @throws {TypeError} 如果密码无效
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
encryptAad: (data: BufferSource, aad: BufferSource | undefined, passphrase: string) => Promise<Uint8Array<ArrayBuffer>>,
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* 解密数据,包含不加密的附加数据
|
|
17
|
+
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
17
18
|
* @throws {TypeError} 如果密码无效
|
|
18
19
|
*/
|
|
19
|
-
|
|
20
|
+
decryptAad: (data: BufferSource, passphrase: string) => Promise<import("./common.js").PlainData>;
|
|
21
|
+
/**
|
|
22
|
+
* 加密数据
|
|
23
|
+
* @throws {TypeError} 如果密码无效
|
|
24
|
+
*/
|
|
25
|
+
export declare function encrypt(data: BufferSource, passphrase: string): Promise<Uint8Array<ArrayBuffer>>;
|
|
20
26
|
/**
|
|
21
27
|
* 解密数据
|
|
22
28
|
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
23
29
|
* @throws {TypeError} 如果密码无效
|
|
24
30
|
*/
|
|
25
|
-
decrypt
|
|
31
|
+
export declare function decrypt(data: BufferSource, passphrase: string): Promise<Uint8Array<ArrayBuffer>>;
|
|
26
32
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/encryption/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,0BAA0B;AAC1B,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAEvD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/encryption/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,0BAA0B;AAC1B,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAEvD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS,CAQlF;AAED,eAAO;AACH;;;GAGG;AAEH,UAAU;AACV;;;;GAIG;AAEH,UAAU,sFACQ,CAAC;AAEvB;;;GAGG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAEtG;AACD;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAGtG"}
|
package/dist/encryption/index.js
CHANGED
|
@@ -22,22 +22,32 @@ export function extractAad(data) {
|
|
|
22
22
|
}
|
|
23
23
|
export const {
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* 加密数据,包含不加密的附加数据
|
|
26
26
|
* @throws {TypeError} 如果密码无效
|
|
27
27
|
*/
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
29
|
-
|
|
29
|
+
encryptAad,
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* 解密数据,包含不加密的附加数据
|
|
32
|
+
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
32
33
|
* @throws {TypeError} 如果密码无效
|
|
33
34
|
*/
|
|
34
35
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
35
|
-
|
|
36
|
+
decryptAad, } = createModule(impl);
|
|
37
|
+
/**
|
|
38
|
+
* 加密数据
|
|
39
|
+
* @throws {TypeError} 如果密码无效
|
|
40
|
+
*/
|
|
41
|
+
export async function encrypt(data, passphrase) {
|
|
42
|
+
return await encryptAad(data, undefined, passphrase);
|
|
43
|
+
}
|
|
36
44
|
/**
|
|
37
45
|
* 解密数据
|
|
38
46
|
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
39
47
|
* @throws {TypeError} 如果密码无效
|
|
40
48
|
*/
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
export async function decrypt(data, passphrase) {
|
|
50
|
+
const decrypted = await decryptAad(data, passphrase);
|
|
51
|
+
return decrypted.data;
|
|
52
|
+
}
|
|
43
53
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/encryption/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC1C,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAkB;IACzC,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IACtC,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM;AACT;;;GAGG;AACH,6DAA6D;AAC7D,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/encryption/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC1C,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAkB;IACzC,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IACtC,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM;AACT;;;GAGG;AACH,6DAA6D;AAC7D,UAAU;AACV;;;;GAIG;AACH,6DAA6D;AAC7D,UAAU,GACb,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;AAEvB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAkB,EAAE,UAAkB;IAChE,OAAO,MAAM,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AACzD,CAAC;AACD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAkB,EAAE,UAAkB;IAChE,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACrD,OAAO,SAAS,CAAC,IAAI,CAAC;AAC1B,CAAC"}
|
|
@@ -1,21 +1,17 @@
|
|
|
1
|
+
import { type PlainData } from './common.js';
|
|
1
2
|
/** 模块 */
|
|
2
3
|
interface Module {
|
|
3
|
-
/**
|
|
4
|
-
* 加密数据
|
|
5
|
-
* @throws {TypeError} 如果密码无效
|
|
6
|
-
*/
|
|
7
|
-
encrypt(data: BufferSource, passphrase: string): Promise<Uint8Array<ArrayBuffer>>;
|
|
8
4
|
/**
|
|
9
5
|
* 加密数据,包含不加密的附加数据
|
|
10
6
|
* @throws {TypeError} 如果密码无效
|
|
11
7
|
*/
|
|
12
8
|
encryptAad(data: BufferSource, aad: BufferSource | undefined, passphrase: string): Promise<Uint8Array<ArrayBuffer>>;
|
|
13
9
|
/**
|
|
14
|
-
*
|
|
10
|
+
* 解密数据,包含不加密的附加数据
|
|
15
11
|
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
16
12
|
* @throws {TypeError} 如果密码无效
|
|
17
13
|
*/
|
|
18
|
-
|
|
14
|
+
decryptAad(data: BufferSource, passphrase: string): Promise<PlainData>;
|
|
19
15
|
}
|
|
20
16
|
/** 创建模块 */
|
|
21
17
|
export declare function createModule(impl: typeof import('#encryption') | typeof import('./wasm.js')): Module;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/encryption/module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/encryption/module.ts"],"names":[],"mappings":"AACA,OAAO,EAQH,KAAK,SAAS,EACjB,MAAM,aAAa,CAAC;AAYrB,SAAS;AACT,UAAU,MAAM;IACZ;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IACpH;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CAC1E;AAED,WAAW;AACX,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,aAAa,CAAC,GAAG,cAAc,WAAW,CAAC,GAAG,MAAM,CA8CpG"}
|
|
@@ -36,27 +36,22 @@ export function createModule(impl) {
|
|
|
36
36
|
result.set(encrypted.data, MAGIC_NUMBER.length + NONCE_SIZE + AAD_LEN_SIZE + paddedAddSize);
|
|
37
37
|
return result;
|
|
38
38
|
};
|
|
39
|
-
const
|
|
40
|
-
return await encryptAad(data, undefined, passphrase);
|
|
41
|
-
};
|
|
42
|
-
const decrypt = async (data, passphrase) => {
|
|
39
|
+
const decryptAad = async (data, passphrase) => {
|
|
43
40
|
assertPassphrase(passphrase);
|
|
44
41
|
const encrypted = parseEncrypted(data);
|
|
45
42
|
if (encrypted == null) {
|
|
46
43
|
throw new TypeError('Invalid encrypted data');
|
|
47
44
|
}
|
|
48
45
|
try {
|
|
49
|
-
|
|
50
|
-
return result.data;
|
|
46
|
+
return await impl.decrypt(encrypted, passphrase);
|
|
51
47
|
}
|
|
52
48
|
catch (ex) {
|
|
53
49
|
throw new Error('Wrong passphrase', { cause: ex });
|
|
54
50
|
}
|
|
55
51
|
};
|
|
56
52
|
return {
|
|
57
|
-
encrypt,
|
|
58
53
|
encryptAad,
|
|
59
|
-
|
|
54
|
+
decryptAad,
|
|
60
55
|
};
|
|
61
56
|
}
|
|
62
57
|
//# sourceMappingURL=module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/encryption/module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACH,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,OAAO,EACP,cAAc,GAEjB,MAAM,aAAa,CAAC;AAErB,WAAW;AACX,SAAS,gBAAgB,CAAC,UAAkB;IACxC,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,SAAS,CAAC,uCAAuC,CAAC,CAAC;IACjE,CAAC;AACL,CAAC;
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/encryption/module.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { isEncrypted, encrypt, decrypt, encryptAad, extractAad } from './encryption/index.js';
|
|
1
|
+
export { isEncrypted, encrypt, decrypt, encryptAad, extractAad, decryptAad } from './encryption/index.js';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { isEncrypted, encrypt, decrypt, encryptAad, extractAad } from './encryption/index.js';
|
|
1
|
+
export { isEncrypted, encrypt, decrypt, encryptAad, extractAad, decryptAad } from './encryption/index.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EncryptedData, PlainData } from './common.js';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/** 动态选择加密模块 */
|
|
4
|
+
async function loadModule() {
|
|
4
5
|
if (
|
|
5
6
|
typeof crypto == 'object' &&
|
|
6
7
|
typeof crypto.subtle == 'object' &&
|
|
@@ -13,7 +14,21 @@ const module = async () => {
|
|
|
13
14
|
} else {
|
|
14
15
|
return import('./wasm.js');
|
|
15
16
|
}
|
|
16
|
-
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let module: typeof loadModule;
|
|
20
|
+
|
|
21
|
+
/** For test purposes */
|
|
22
|
+
export function resetModuleLoader(): void {
|
|
23
|
+
module = async () => {
|
|
24
|
+
const mod = await loadModule();
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
26
|
+
module = () => Promise.resolve(mod);
|
|
27
|
+
return mod;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
resetModuleLoader();
|
|
17
32
|
|
|
18
33
|
/** browser encrypt */
|
|
19
34
|
export async function encrypt(data: PlainData, passphrase: string): Promise<EncryptedData> {
|
package/src/encryption/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ export function isEncrypted(data: BufferSource): boolean {
|
|
|
13
13
|
* 从加密数据中提取附加数据
|
|
14
14
|
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
15
15
|
*/
|
|
16
|
-
export function extractAad(data: BufferSource): Uint8Array | undefined {
|
|
16
|
+
export function extractAad(data: BufferSource): Uint8Array<ArrayBuffer> | undefined {
|
|
17
17
|
const encrypted = parseEncrypted(data);
|
|
18
18
|
if (encrypted == null) {
|
|
19
19
|
throw new TypeError('Invalid encrypted data');
|
|
@@ -24,12 +24,6 @@ export function extractAad(data: BufferSource): Uint8Array | undefined {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export const {
|
|
27
|
-
/**
|
|
28
|
-
* 加密数据
|
|
29
|
-
* @throws {TypeError} 如果密码无效
|
|
30
|
-
*/
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
32
|
-
encrypt,
|
|
33
27
|
/**
|
|
34
28
|
* 加密数据,包含不加密的附加数据
|
|
35
29
|
* @throws {TypeError} 如果密码无效
|
|
@@ -37,10 +31,27 @@ export const {
|
|
|
37
31
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
38
32
|
encryptAad,
|
|
39
33
|
/**
|
|
40
|
-
*
|
|
34
|
+
* 解密数据,包含不加密的附加数据
|
|
41
35
|
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
42
36
|
* @throws {TypeError} 如果密码无效
|
|
43
37
|
*/
|
|
44
38
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
45
|
-
|
|
39
|
+
decryptAad,
|
|
46
40
|
} = createModule(impl);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 加密数据
|
|
44
|
+
* @throws {TypeError} 如果密码无效
|
|
45
|
+
*/
|
|
46
|
+
export async function encrypt(data: BufferSource, passphrase: string): Promise<Uint8Array<ArrayBuffer>> {
|
|
47
|
+
return await encryptAad(data, undefined, passphrase);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 解密数据
|
|
51
|
+
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
52
|
+
* @throws {TypeError} 如果密码无效
|
|
53
|
+
*/
|
|
54
|
+
export async function decrypt(data: BufferSource, passphrase: string): Promise<Uint8Array<ArrayBuffer>> {
|
|
55
|
+
const decrypted = await decryptAad(data, passphrase);
|
|
56
|
+
return decrypted.data;
|
|
57
|
+
}
|
package/src/encryption/module.ts
CHANGED
|
@@ -22,22 +22,17 @@ function assertPassphrase(passphrase: string): void {
|
|
|
22
22
|
|
|
23
23
|
/** 模块 */
|
|
24
24
|
interface Module {
|
|
25
|
-
/**
|
|
26
|
-
* 加密数据
|
|
27
|
-
* @throws {TypeError} 如果密码无效
|
|
28
|
-
*/
|
|
29
|
-
encrypt(data: BufferSource, passphrase: string): Promise<Uint8Array<ArrayBuffer>>;
|
|
30
25
|
/**
|
|
31
26
|
* 加密数据,包含不加密的附加数据
|
|
32
27
|
* @throws {TypeError} 如果密码无效
|
|
33
28
|
*/
|
|
34
29
|
encryptAad(data: BufferSource, aad: BufferSource | undefined, passphrase: string): Promise<Uint8Array<ArrayBuffer>>;
|
|
35
30
|
/**
|
|
36
|
-
*
|
|
31
|
+
* 解密数据,包含不加密的附加数据
|
|
37
32
|
* @throws {TypeError} 如果数据不是有效的加密数据
|
|
38
33
|
* @throws {TypeError} 如果密码无效
|
|
39
34
|
*/
|
|
40
|
-
|
|
35
|
+
decryptAad(data: BufferSource, passphrase: string): Promise<PlainData>;
|
|
41
36
|
}
|
|
42
37
|
|
|
43
38
|
/** 创建模块 */
|
|
@@ -69,26 +64,22 @@ export function createModule(impl: typeof import('#encryption') | typeof import(
|
|
|
69
64
|
result.set(encrypted.data, MAGIC_NUMBER.length + NONCE_SIZE + AAD_LEN_SIZE + paddedAddSize);
|
|
70
65
|
return result;
|
|
71
66
|
};
|
|
72
|
-
const encrypt: Module['encrypt'] = async (data, passphrase) => {
|
|
73
|
-
return await encryptAad(data, undefined, passphrase);
|
|
74
|
-
};
|
|
75
67
|
|
|
76
|
-
const
|
|
68
|
+
const decryptAad: Module['decryptAad'] = async (data, passphrase) => {
|
|
77
69
|
assertPassphrase(passphrase);
|
|
78
70
|
const encrypted = parseEncrypted(data);
|
|
79
71
|
if (encrypted == null) {
|
|
80
72
|
throw new TypeError('Invalid encrypted data');
|
|
81
73
|
}
|
|
82
74
|
try {
|
|
83
|
-
|
|
84
|
-
return result.data;
|
|
75
|
+
return await impl.decrypt(encrypted, passphrase);
|
|
85
76
|
} catch (ex) {
|
|
86
77
|
throw new Error('Wrong passphrase', { cause: ex });
|
|
87
78
|
}
|
|
88
79
|
};
|
|
80
|
+
|
|
89
81
|
return {
|
|
90
|
-
encrypt,
|
|
91
82
|
encryptAad,
|
|
92
|
-
|
|
83
|
+
decryptAad,
|
|
93
84
|
};
|
|
94
85
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { isEncrypted, encrypt, decrypt, encryptAad, extractAad } from './encryption/index.js';
|
|
1
|
+
export { isEncrypted, encrypt, decrypt, encryptAad, extractAad, decryptAad } from './encryption/index.js';
|
package/tests/.check.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { isEncrypted, extractAad, type encryptAad as e, type decryptAad as d } from '../dist/index.js';
|
|
3
|
+
import { toUint8Array } from '../dist/utils.js';
|
|
4
|
+
import type * as nodeImpl from '../dist/encryption/node.js';
|
|
5
|
+
import type * as webImpl from '../dist/encryption/web.js';
|
|
6
|
+
import type * as wasmImpl from '../dist/encryption/wasm.js';
|
|
7
|
+
|
|
8
|
+
const data = Object.entries({
|
|
9
|
+
empty: Buffer.from(''),
|
|
10
|
+
short: Buffer.from('Hello, World!'),
|
|
11
|
+
long: Buffer.from('Hello, World!'.repeat(100)),
|
|
12
|
+
zeros: new Uint8Array(100),
|
|
13
|
+
longlong: Buffer.from('Hello, World!'.repeat(1000)).buffer,
|
|
14
|
+
unaligned: Buffer.from('Hello, World!'.repeat(1000)).subarray(1),
|
|
15
|
+
}).map(([k, d]) => ({ raw: d, length: d.byteLength, type: d.constructor.name, tag: k }));
|
|
16
|
+
|
|
17
|
+
const passphrase = randomUUID();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 检查实现模块
|
|
21
|
+
*/
|
|
22
|
+
export function checkModule(module: { decryptAad: typeof d; encryptAad: typeof e }): void {
|
|
23
|
+
it('has correct exports', () => {
|
|
24
|
+
expect(module).toMatchObject({
|
|
25
|
+
decryptAad: expect.any(Function) as unknown,
|
|
26
|
+
encryptAad: expect.any(Function) as unknown,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const { decryptAad, encryptAad } = module;
|
|
31
|
+
|
|
32
|
+
it.each(data)('encrypt/decrypt $type[$length] ($tag)', async ({ raw }) => {
|
|
33
|
+
const encrypted = await encryptAad(raw, undefined, passphrase);
|
|
34
|
+
expect(encrypted).toBeInstanceOf(Uint8Array);
|
|
35
|
+
expect(encrypted.byteLength).toBeGreaterThan(raw.byteLength);
|
|
36
|
+
expect(isEncrypted(encrypted)).toBe(true);
|
|
37
|
+
|
|
38
|
+
await expect(async () => {
|
|
39
|
+
await decryptAad(encrypted, 'xx');
|
|
40
|
+
}).rejects.toThrow();
|
|
41
|
+
|
|
42
|
+
const decrypted = await decryptAad(encrypted, passphrase);
|
|
43
|
+
expect(decrypted.data).toBeInstanceOf(Uint8Array);
|
|
44
|
+
expect(decrypted.data).toEqual(toUint8Array(raw));
|
|
45
|
+
expect(decrypted.aad).toBeInstanceOf(Uint8Array);
|
|
46
|
+
expect(decrypted.aad.byteLength).toBe(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it.each(data)('encrypt/decrypt $type[$length] ($tag) with aad', async ({ raw }) => {
|
|
50
|
+
const aad = Buffer.from('Hello, AAD!');
|
|
51
|
+
const encrypted = await encryptAad(raw, aad, passphrase);
|
|
52
|
+
expect(encrypted).toBeInstanceOf(Uint8Array);
|
|
53
|
+
expect(encrypted.byteLength).toBeGreaterThan(raw.byteLength);
|
|
54
|
+
expect(isEncrypted(encrypted)).toBe(true);
|
|
55
|
+
|
|
56
|
+
const extractedAad = extractAad(encrypted);
|
|
57
|
+
expect(extractedAad).toBeInstanceOf(Uint8Array);
|
|
58
|
+
expect(extractedAad).toEqual(toUint8Array(aad));
|
|
59
|
+
|
|
60
|
+
await expect(async () => {
|
|
61
|
+
await decryptAad(encrypted, 'xx');
|
|
62
|
+
}).rejects.toThrow();
|
|
63
|
+
|
|
64
|
+
const decrypted = await decryptAad(encrypted, passphrase);
|
|
65
|
+
expect(decrypted.data).toBeInstanceOf(Uint8Array);
|
|
66
|
+
expect(decrypted.data).toEqual(toUint8Array(raw));
|
|
67
|
+
expect(decrypted.aad).toBeInstanceOf(Uint8Array);
|
|
68
|
+
expect(decrypted.aad).toEqual(toUint8Array(aad));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 检查实现
|
|
74
|
+
*/
|
|
75
|
+
export function checkImplEncryption(
|
|
76
|
+
encrypt: typeof webImpl.encrypt | typeof wasmImpl.encrypt | typeof nodeImpl.encrypt,
|
|
77
|
+
decrypt: typeof webImpl.decrypt | typeof wasmImpl.decrypt | typeof nodeImpl.decrypt,
|
|
78
|
+
): void {
|
|
79
|
+
it.each(data)(
|
|
80
|
+
`$type[$length] ($tag)`,
|
|
81
|
+
async ({ raw }) => {
|
|
82
|
+
const encrypted = await encrypt({ data: toUint8Array(raw), aad: new Uint8Array(0) }, passphrase);
|
|
83
|
+
expect(encrypted.nonce).toBeInstanceOf(Uint8Array);
|
|
84
|
+
expect(encrypted.nonce.byteLength).toBe(12);
|
|
85
|
+
expect(encrypted.aad).toBeInstanceOf(Uint8Array);
|
|
86
|
+
expect(encrypted.aad.byteLength).toBe(0);
|
|
87
|
+
expect(encrypted.data).toBeInstanceOf(Uint8Array);
|
|
88
|
+
|
|
89
|
+
await expect(async () => {
|
|
90
|
+
await decrypt(encrypted, 'xx');
|
|
91
|
+
}).rejects.toThrow();
|
|
92
|
+
|
|
93
|
+
const decrypted = await decrypt(encrypted, passphrase);
|
|
94
|
+
expect(decrypted.data).toBeInstanceOf(Uint8Array);
|
|
95
|
+
expect(decrypted.data).toEqual(toUint8Array(raw));
|
|
96
|
+
expect(decrypted.aad).toBeInstanceOf(Uint8Array);
|
|
97
|
+
expect(decrypted.aad.byteLength).toBe(0);
|
|
98
|
+
},
|
|
99
|
+
100_000,
|
|
100
|
+
);
|
|
101
|
+
it.each(data)(
|
|
102
|
+
`(aad) $type[$length] ($tag)`,
|
|
103
|
+
async ({ raw }) => {
|
|
104
|
+
const aad = Buffer.from('Hello, AAD!');
|
|
105
|
+
const encrypted = await encrypt({ data: toUint8Array(raw), aad }, passphrase);
|
|
106
|
+
expect(encrypted.nonce).toBeInstanceOf(Uint8Array);
|
|
107
|
+
expect(encrypted.nonce.byteLength).toBe(12);
|
|
108
|
+
expect(encrypted.data).toBeInstanceOf(Uint8Array);
|
|
109
|
+
|
|
110
|
+
await expect(async () => {
|
|
111
|
+
await decrypt(
|
|
112
|
+
// @ts-expect-error no aad
|
|
113
|
+
{
|
|
114
|
+
nonce: encrypted.nonce,
|
|
115
|
+
data: encrypted.data,
|
|
116
|
+
},
|
|
117
|
+
passphrase,
|
|
118
|
+
);
|
|
119
|
+
}).rejects.toThrow();
|
|
120
|
+
|
|
121
|
+
const decrypted = await decrypt(
|
|
122
|
+
{
|
|
123
|
+
nonce: encrypted.nonce,
|
|
124
|
+
aad: toUint8Array(aad),
|
|
125
|
+
data: encrypted.data,
|
|
126
|
+
},
|
|
127
|
+
passphrase,
|
|
128
|
+
);
|
|
129
|
+
expect(decrypted.data).toBeInstanceOf(Uint8Array);
|
|
130
|
+
expect(decrypted.data).toEqual(toUint8Array(raw));
|
|
131
|
+
},
|
|
132
|
+
100_000,
|
|
133
|
+
);
|
|
134
|
+
}
|
package/tests/browser.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createModule } from '../dist/encryption/module.js';
|
|
2
|
+
import * as browserImpl from '../dist/encryption/browser.js';
|
|
3
|
+
|
|
4
|
+
import { checkModule } from './.check.ts';
|
|
5
|
+
|
|
6
|
+
describe('Encryption impl browser', () => {
|
|
7
|
+
const { subtle } = crypto;
|
|
8
|
+
describe('should work with crypto subtle', () => {
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
browserImpl.resetModuleLoader();
|
|
11
|
+
});
|
|
12
|
+
test('subtle exists', () => {
|
|
13
|
+
expect(crypto.subtle).toEqual(expect.any(Object));
|
|
14
|
+
});
|
|
15
|
+
checkModule(createModule(browserImpl));
|
|
16
|
+
});
|
|
17
|
+
describe('should work without crypto subtle', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
Object.defineProperty(crypto, 'subtle', { value: undefined, configurable: true });
|
|
20
|
+
browserImpl.resetModuleLoader();
|
|
21
|
+
});
|
|
22
|
+
afterAll(() => {
|
|
23
|
+
Object.defineProperty(crypto, 'subtle', { value: subtle, configurable: true });
|
|
24
|
+
browserImpl.resetModuleLoader();
|
|
25
|
+
});
|
|
26
|
+
checkModule(createModule(browserImpl));
|
|
27
|
+
});
|
|
28
|
+
});
|
package/tests/encryption.ts
CHANGED
|
@@ -1,21 +1,8 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { MAGIC_NUMBER } from '../dist/encryption/index.js';
|
|
2
|
-
import { isEncrypted, encrypt, decrypt, encryptAad, extractAad } from '../dist/index.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import * as nodeImpl from '../dist/encryption/node.js';
|
|
6
|
-
import * as browserImpl from '../dist/encryption/browser.js';
|
|
7
|
-
import * as webImpl from '../dist/encryption/web.js';
|
|
8
|
-
import * as wasmImpl from '../dist/encryption/wasm.js';
|
|
9
|
-
|
|
10
|
-
const data = Object.entries({
|
|
11
|
-
empty: Buffer.from(''),
|
|
12
|
-
short: Buffer.from('Hello, World!'),
|
|
13
|
-
long: Buffer.from('Hello, World!'.repeat(100)),
|
|
14
|
-
zeros: new Uint8Array(100),
|
|
15
|
-
longlong: Buffer.from('Hello, World!'.repeat(1000)).buffer,
|
|
16
|
-
unaligned: Buffer.from('Hello, World!'.repeat(1000)).subarray(1),
|
|
17
|
-
}).map(([k, d]) => ({ raw: d, length: d.byteLength, type: d.constructor.name, tag: k }));
|
|
18
|
-
const passphrase = 'test';
|
|
3
|
+
import { isEncrypted, encrypt, decrypt, encryptAad, decryptAad, extractAad } from '../dist/index.js';
|
|
4
|
+
|
|
5
|
+
const passphrase = randomUUID();
|
|
19
6
|
|
|
20
7
|
describe('Encryption root export', () => {
|
|
21
8
|
it('has MAGIC_NUMBER', () => {
|
|
@@ -64,6 +51,18 @@ describe('Encryption root export', () => {
|
|
|
64
51
|
await expect(async () => decrypt(Buffer.alloc(100), 'xx')).rejects.toThrow('Invalid encrypted data');
|
|
65
52
|
});
|
|
66
53
|
|
|
54
|
+
it('encrypt/decrypt', async () => {
|
|
55
|
+
const raw = Buffer.from('Hello, World!');
|
|
56
|
+
const encrypted = await encrypt(raw, passphrase);
|
|
57
|
+
expect(encrypted).toBeInstanceOf(Uint8Array);
|
|
58
|
+
expect(isEncrypted(encrypted)).toBe(true);
|
|
59
|
+
|
|
60
|
+
const decrypted = await decrypt(encrypted, passphrase);
|
|
61
|
+
expect(decrypted).toBeInstanceOf(Uint8Array);
|
|
62
|
+
expect(decrypted).toEqual(raw);
|
|
63
|
+
expect(decrypted).not.toBe(raw);
|
|
64
|
+
});
|
|
65
|
+
|
|
67
66
|
describe('aad', () => {
|
|
68
67
|
it('accepts empty aad', async () => {
|
|
69
68
|
const encrypted = await encryptAad(Buffer.alloc(0), Buffer.alloc(0), passphrase);
|
|
@@ -79,166 +78,37 @@ describe('Encryption root export', () => {
|
|
|
79
78
|
expect(extractedAad).toBeUndefined();
|
|
80
79
|
});
|
|
81
80
|
|
|
82
|
-
it('
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('rejects invalid data', () => {
|
|
90
|
-
const data = Buffer.alloc(10);
|
|
91
|
-
expect(() => extractAad(data)).toThrow('Invalid encrypted data');
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
checkModule({ encrypt, decrypt, encryptAad });
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* 检查实现模块
|
|
100
|
-
*/
|
|
101
|
-
function checkModule(module: { encrypt: typeof encrypt; decrypt: typeof decrypt; encryptAad: typeof encryptAad }) {
|
|
102
|
-
it('has correct exports', () => {
|
|
103
|
-
expect(module).toMatchObject({
|
|
104
|
-
encrypt: expect.any(Function) as unknown,
|
|
105
|
-
decrypt: expect.any(Function) as unknown,
|
|
106
|
-
encryptAad: expect.any(Function) as unknown,
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const { encrypt, decrypt, encryptAad } = module;
|
|
111
|
-
|
|
112
|
-
it.each(data)('encrypt/decrypt $type[$length] ($tag)', async ({ raw }) => {
|
|
113
|
-
const encrypted = await encrypt(raw, passphrase);
|
|
114
|
-
expect(encrypted).toBeInstanceOf(Uint8Array);
|
|
115
|
-
expect(encrypted.byteLength).toBeGreaterThan(raw.byteLength);
|
|
116
|
-
expect(isEncrypted(encrypted)).toBe(true);
|
|
117
|
-
|
|
118
|
-
await expect(async () => {
|
|
119
|
-
await decrypt(encrypted, 'xx');
|
|
120
|
-
}).rejects.toThrow();
|
|
121
|
-
|
|
122
|
-
const decrypted = await decrypt(encrypted, passphrase);
|
|
123
|
-
expect(decrypted).toBeInstanceOf(Uint8Array);
|
|
124
|
-
expect(decrypted).toEqual(toUint8Array(raw));
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it.each(data)('encrypt/decrypt $type[$length] ($tag) with aad', async ({ raw }) => {
|
|
128
|
-
const aad = Buffer.from('Hello, AAD!');
|
|
129
|
-
const encrypted = await encryptAad(raw, aad, passphrase);
|
|
130
|
-
expect(encrypted).toBeInstanceOf(Uint8Array);
|
|
131
|
-
expect(encrypted.byteLength).toBeGreaterThan(raw.byteLength);
|
|
132
|
-
expect(isEncrypted(encrypted)).toBe(true);
|
|
133
|
-
|
|
134
|
-
const extractedAad = extractAad(encrypted);
|
|
135
|
-
expect(extractedAad).toBeInstanceOf(Uint8Array);
|
|
136
|
-
expect(extractedAad).toEqual(toUint8Array(aad));
|
|
137
|
-
|
|
138
|
-
await expect(async () => {
|
|
139
|
-
await decrypt(encrypted, 'xx');
|
|
140
|
-
}).rejects.toThrow();
|
|
81
|
+
it('encrypts and decrypts with aad', async () => {
|
|
82
|
+
const raw = Buffer.from('Hello, World!');
|
|
83
|
+
const aad = Buffer.from('Hello, AAD!');
|
|
84
|
+
const encrypted = await encryptAad(raw, aad, passphrase);
|
|
85
|
+
expect(encrypted).toBeInstanceOf(Uint8Array);
|
|
141
86
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* 检查实现
|
|
150
|
-
*/
|
|
151
|
-
function checkImplEncryption(
|
|
152
|
-
encrypt: typeof webImpl.encrypt | typeof wasmImpl.encrypt | typeof nodeImpl.encrypt,
|
|
153
|
-
decrypt: typeof webImpl.decrypt | typeof wasmImpl.decrypt | typeof nodeImpl.decrypt,
|
|
154
|
-
) {
|
|
155
|
-
it.each(data)(
|
|
156
|
-
`$type[$length] ($tag)`,
|
|
157
|
-
async ({ raw }) => {
|
|
158
|
-
const encrypted = await encrypt({ data: toUint8Array(raw), aad: new Uint8Array(0) }, passphrase);
|
|
159
|
-
expect(encrypted.nonce).toBeInstanceOf(Uint8Array);
|
|
160
|
-
expect(encrypted.nonce.byteLength).toBe(12);
|
|
161
|
-
expect(encrypted.aad).toBeInstanceOf(Uint8Array);
|
|
162
|
-
expect(encrypted.aad.byteLength).toBe(0);
|
|
163
|
-
expect(encrypted.data).toBeInstanceOf(Uint8Array);
|
|
87
|
+
const extractedAad = extractAad(encrypted);
|
|
88
|
+
expect(extractedAad).toBeInstanceOf(Uint8Array);
|
|
89
|
+
expect(extractedAad).toEqual(aad);
|
|
164
90
|
|
|
165
91
|
await expect(async () => {
|
|
166
|
-
await
|
|
92
|
+
await decryptAad(encrypted, 'xx');
|
|
167
93
|
}).rejects.toThrow();
|
|
168
94
|
|
|
169
|
-
const decrypted = await
|
|
95
|
+
const decrypted = await decryptAad(encrypted, passphrase);
|
|
170
96
|
expect(decrypted.data).toBeInstanceOf(Uint8Array);
|
|
171
|
-
expect(decrypted.data).toEqual(
|
|
97
|
+
expect(decrypted.data).toEqual(raw);
|
|
172
98
|
expect(decrypted.aad).toBeInstanceOf(Uint8Array);
|
|
173
|
-
expect(decrypted.aad
|
|
174
|
-
}
|
|
175
|
-
100_000,
|
|
176
|
-
);
|
|
177
|
-
it.each(data)(
|
|
178
|
-
`(aad) $type[$length] ($tag)`,
|
|
179
|
-
async ({ raw }) => {
|
|
180
|
-
const aad = Buffer.from('Hello, AAD!');
|
|
181
|
-
const encrypted = await encrypt({ data: toUint8Array(raw), aad }, passphrase);
|
|
182
|
-
expect(encrypted.nonce).toBeInstanceOf(Uint8Array);
|
|
183
|
-
expect(encrypted.nonce.byteLength).toBe(12);
|
|
184
|
-
expect(encrypted.data).toBeInstanceOf(Uint8Array);
|
|
99
|
+
expect(decrypted.aad).toEqual(aad);
|
|
100
|
+
});
|
|
185
101
|
|
|
102
|
+
it('rejects invalid aad size', async () => {
|
|
103
|
+
const aad2 = Buffer.alloc(1024 * 1024 * 1024 + 1);
|
|
186
104
|
await expect(async () => {
|
|
187
|
-
await
|
|
188
|
-
|
|
189
|
-
{
|
|
190
|
-
nonce: encrypted.nonce,
|
|
191
|
-
data: encrypted.data,
|
|
192
|
-
},
|
|
193
|
-
passphrase,
|
|
194
|
-
);
|
|
195
|
-
}).rejects.toThrow();
|
|
196
|
-
|
|
197
|
-
const decrypted = await decrypt(
|
|
198
|
-
{
|
|
199
|
-
nonce: encrypted.nonce,
|
|
200
|
-
aad: toUint8Array(aad),
|
|
201
|
-
data: encrypted.data,
|
|
202
|
-
},
|
|
203
|
-
passphrase,
|
|
204
|
-
);
|
|
205
|
-
expect(decrypted.data).toBeInstanceOf(Uint8Array);
|
|
206
|
-
expect(decrypted.data).toEqual(toUint8Array(raw));
|
|
207
|
-
},
|
|
208
|
-
100_000,
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
describe('Encryption impl', () => {
|
|
213
|
-
const impls = Object.entries({
|
|
214
|
-
node: nodeImpl,
|
|
215
|
-
browser: browserImpl,
|
|
216
|
-
web: webImpl,
|
|
217
|
-
wasm: wasmImpl,
|
|
218
|
-
});
|
|
219
|
-
describe.each(impls)('impl %s', (name, impl) => {
|
|
220
|
-
const module = createModule(impl);
|
|
221
|
-
checkModule(module);
|
|
222
|
-
});
|
|
223
|
-
describe.each(impls.slice(1))(`cross impl %s/${impls[0][0]}`, (name, impl) => {
|
|
224
|
-
describe(`${impls[0][0]} -> ${name}`, () => {
|
|
225
|
-
checkImplEncryption(impls[0][1].encrypt, impl.decrypt);
|
|
226
|
-
});
|
|
227
|
-
describe(`${name} -> ${impls[0][0]}`, () => {
|
|
228
|
-
checkImplEncryption(impl.encrypt, impls[0][1].decrypt);
|
|
105
|
+
await encryptAad(Buffer.alloc(0), aad2, passphrase);
|
|
106
|
+
}).rejects.toThrow('Invalid AAD size');
|
|
229
107
|
});
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
108
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
beforeAll(() => {
|
|
237
|
-
Object.defineProperty(crypto, 'subtle', { value: undefined, configurable: true });
|
|
238
|
-
});
|
|
239
|
-
afterAll(() => {
|
|
240
|
-
Object.defineProperty(crypto, 'subtle', { value: subtle, configurable: true });
|
|
109
|
+
it('rejects invalid data', () => {
|
|
110
|
+
const data = Buffer.alloc(10);
|
|
111
|
+
expect(() => extractAad(data)).toThrow('Invalid encrypted data');
|
|
241
112
|
});
|
|
242
|
-
checkModule(createModule(browserImpl));
|
|
243
113
|
});
|
|
244
114
|
});
|
package/tests/modules.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { encryptAad, decryptAad } from '../dist/index.js';
|
|
2
|
+
import { createModule } from '../dist/encryption/module.js';
|
|
3
|
+
import * as nodeImpl from '../dist/encryption/node.js';
|
|
4
|
+
import * as webImpl from '../dist/encryption/web.js';
|
|
5
|
+
import * as wasmImpl from '../dist/encryption/wasm.js';
|
|
6
|
+
|
|
7
|
+
import { checkImplEncryption, checkModule } from './.check.ts';
|
|
8
|
+
|
|
9
|
+
describe('Encryption impl', () => {
|
|
10
|
+
const impls = Object.entries({
|
|
11
|
+
node: nodeImpl,
|
|
12
|
+
web: webImpl,
|
|
13
|
+
wasm: wasmImpl,
|
|
14
|
+
});
|
|
15
|
+
describe('root exports', () => {
|
|
16
|
+
checkModule({ encryptAad, decryptAad });
|
|
17
|
+
});
|
|
18
|
+
describe.each(impls)('impl %s', (name, impl) => {
|
|
19
|
+
const module = createModule(impl);
|
|
20
|
+
checkModule(module);
|
|
21
|
+
});
|
|
22
|
+
describe.each(impls.slice(1))(`cross impl %s/${impls[0][0]}`, (name, impl) => {
|
|
23
|
+
describe(`${impls[0][0]} -> ${name}`, () => {
|
|
24
|
+
checkImplEncryption(impls[0][1].encrypt, impl.decrypt);
|
|
25
|
+
});
|
|
26
|
+
describe(`${name} -> ${impls[0][0]}`, () => {
|
|
27
|
+
checkImplEncryption(impl.encrypt, impls[0][1].decrypt);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|