@vex-chat/crypto 11.0.0 → 12.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/README.md +5 -20
- package/dist/index.d.ts +14 -58
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -395
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/src/__tests__/xAsyncApi.extended.test.ts +3 -44
- package/src/__tests__/xAsyncProfile.ts +6 -26
- package/src/index.ts +44 -616
- package/src/__tests__/cryptoProfile.ts +0 -74
- package/src/__tests__/cryptoProfileScope.test.ts +0 -77
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) 2020-2026 Vex Heavy Industries LLC
|
|
3
|
-
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
4
|
-
* Commercial licenses available at vex.wtf
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
getCryptoProfile,
|
|
9
|
-
setCryptoProfile,
|
|
10
|
-
xDH,
|
|
11
|
-
xMakeNonce,
|
|
12
|
-
xRandomBytes,
|
|
13
|
-
XUtils,
|
|
14
|
-
} from "../index.js";
|
|
15
|
-
|
|
16
|
-
afterEach(() => {
|
|
17
|
-
setCryptoProfile("tweetnacl");
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("crypto profile defaults to tweetnacl", () => {
|
|
21
|
-
expect(getCryptoProfile()).toBe("tweetnacl");
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("fips profile rejects nacl-coupled operations", () => {
|
|
25
|
-
setCryptoProfile("fips");
|
|
26
|
-
|
|
27
|
-
const myPrivateKey = XUtils.decodeHex(
|
|
28
|
-
"918ed243e2c6c507168b20e8b167cff33a10c30e99e8defe28dc2147f5cce703",
|
|
29
|
-
);
|
|
30
|
-
const theirPublicKey = XUtils.decodeHex(
|
|
31
|
-
"6f4cc1ffc4009bd4f94628ba5922e40afa3491f0daa43ec9da0f7dc39bb1c026",
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
expect(() => xDH(myPrivateKey, theirPublicKey)).toThrow(
|
|
35
|
-
'Crypto profile "fips" does not implement xDH yet.',
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("fips profile still supports random bytes", () => {
|
|
40
|
-
setCryptoProfile("fips");
|
|
41
|
-
|
|
42
|
-
const bytes = xRandomBytes(32);
|
|
43
|
-
const nonce = xMakeNonce();
|
|
44
|
-
|
|
45
|
-
expect(bytes.length).toBe(32);
|
|
46
|
-
expect(nonce.length).toBe(24);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("local storage keys are purpose-separated from identity keys", () => {
|
|
50
|
-
const identity = new Uint8Array(64).fill(7);
|
|
51
|
-
const tweetnacl = XUtils.deriveLocalAtRestAesKey(identity, "tweetnacl");
|
|
52
|
-
const fips = XUtils.deriveLocalAtRestAesKey(identity, "fips");
|
|
53
|
-
|
|
54
|
-
expect(tweetnacl).toHaveLength(32);
|
|
55
|
-
expect(tweetnacl).not.toEqual(identity.slice(0, 32));
|
|
56
|
-
expect(fips).not.toEqual(tweetnacl);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("switching back to tweetnacl restores behavior", () => {
|
|
60
|
-
const myPrivateKey = XUtils.decodeHex(
|
|
61
|
-
"918ed243e2c6c507168b20e8b167cff33a10c30e99e8defe28dc2147f5cce703",
|
|
62
|
-
);
|
|
63
|
-
const theirPublicKey = XUtils.decodeHex(
|
|
64
|
-
"6f4cc1ffc4009bd4f94628ba5922e40afa3491f0daa43ec9da0f7dc39bb1c026",
|
|
65
|
-
);
|
|
66
|
-
const correctDerivedKey =
|
|
67
|
-
"19a8594bdcf875ec9d4b8b9615ea8b73a0b327bb64b8f727dd2dee3a603a2230";
|
|
68
|
-
|
|
69
|
-
setCryptoProfile("fips");
|
|
70
|
-
setCryptoProfile("tweetnacl");
|
|
71
|
-
|
|
72
|
-
const derived = XUtils.encodeHex(xDH(myPrivateKey, theirPublicKey));
|
|
73
|
-
expect(derived).toBe(correctDerivedKey);
|
|
74
|
-
});
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) 2020-2026 Vex Heavy Industries LLC
|
|
3
|
-
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
4
|
-
* Commercial licenses available at vex.wtf
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
enterCryptoProfileScope,
|
|
9
|
-
getCryptoProfile,
|
|
10
|
-
leaveCryptoProfileScope,
|
|
11
|
-
setCryptoProfile,
|
|
12
|
-
xBoxKeyPairAsync,
|
|
13
|
-
xDHAsync,
|
|
14
|
-
xSecretboxAsync,
|
|
15
|
-
xSecretboxOpenAsync,
|
|
16
|
-
XUtils,
|
|
17
|
-
} from "../index.js";
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
setCryptoProfile("tweetnacl");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("leaveCryptoProfileScope without enter throws", () => {
|
|
24
|
-
setCryptoProfile("tweetnacl");
|
|
25
|
-
expect(() => {
|
|
26
|
-
leaveCryptoProfileScope();
|
|
27
|
-
}).toThrow(
|
|
28
|
-
/leaveCryptoProfileScope called without a matching enterCryptoProfileScope/,
|
|
29
|
-
);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("nested enter/leave restores outer profile", () => {
|
|
33
|
-
setCryptoProfile("tweetnacl");
|
|
34
|
-
enterCryptoProfileScope("fips");
|
|
35
|
-
expect(getCryptoProfile()).toBe("fips");
|
|
36
|
-
enterCryptoProfileScope("fips");
|
|
37
|
-
expect(getCryptoProfile()).toBe("fips");
|
|
38
|
-
leaveCryptoProfileScope();
|
|
39
|
-
expect(getCryptoProfile()).toBe("fips");
|
|
40
|
-
leaveCryptoProfileScope();
|
|
41
|
-
expect(getCryptoProfile()).toBe("tweetnacl");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("concurrent fips scopes: overlapping decrypts stay on fips", async () => {
|
|
45
|
-
setCryptoProfile("tweetnacl");
|
|
46
|
-
enterCryptoProfileScope("fips");
|
|
47
|
-
const alice = await xBoxKeyPairAsync();
|
|
48
|
-
const bob = await xBoxKeyPairAsync();
|
|
49
|
-
const shared = await xDHAsync(alice.secretKey, bob.publicKey);
|
|
50
|
-
const nonce = new Uint8Array(24);
|
|
51
|
-
nonce.set(crypto.getRandomValues(new Uint8Array(24)));
|
|
52
|
-
const plaintext = XUtils.decodeUTF8("concurrent-scope");
|
|
53
|
-
const ciphertext = await xSecretboxAsync(plaintext, nonce, shared);
|
|
54
|
-
leaveCryptoProfileScope();
|
|
55
|
-
expect(getCryptoProfile()).toBe("tweetnacl");
|
|
56
|
-
|
|
57
|
-
const decryptOnce = async (delayMs: number): Promise<string> => {
|
|
58
|
-
enterCryptoProfileScope("fips");
|
|
59
|
-
try {
|
|
60
|
-
if (delayMs > 0) {
|
|
61
|
-
await new Promise((r) => setTimeout(r, delayMs));
|
|
62
|
-
}
|
|
63
|
-
const opened = await xSecretboxOpenAsync(ciphertext, nonce, shared);
|
|
64
|
-
if (opened === null) {
|
|
65
|
-
throw new Error("Expected decrypt under stacked fips scope.");
|
|
66
|
-
}
|
|
67
|
-
return XUtils.encodeUTF8(opened);
|
|
68
|
-
} finally {
|
|
69
|
-
leaveCryptoProfileScope();
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const [a, b] = await Promise.all([decryptOnce(0), decryptOnce(15)]);
|
|
74
|
-
expect(a).toBe("concurrent-scope");
|
|
75
|
-
expect(b).toBe("concurrent-scope");
|
|
76
|
-
expect(getCryptoProfile()).toBe("tweetnacl");
|
|
77
|
-
});
|